Since there are infinitely many integers, it is impossible to perform a separate act of naming for each one. Instead, we rely on a system of numeration -- a set of rules for constructing a name for any given number from its value. We'll use the standard decimal system of numeration, with an explicit sign for negative integers.
The following operations belong to my proposed interface for the integer data type. First, the basic arithmetic operations:
negate
Input: negand, an integer.
Output: result, an integer.
Preconditions: none.
Postcondition: The sum of negand and result is
0.
absolute-value
Input: operand, an integer.
Output: result, an integer.
Preconditions: none.
Postcondition: result is the magnitude of
operand, that is, its distance (in either direction) from
0.
add
Inputs: augend and addend, both integers.
Output: sum, an integer.
Preconditions: none.
Postcondition: sum is the sum of augend and
addend.
subtract
Inputs: minuend and subtrahend, both integers.
Output: difference, an integer.
Preconditions: none.
Postcondition: minuend is the sum of difference
and subtrahend.
multiply
Inputs: multiplicand and multiplier, both
integers.
Output: product, an integer.
Preconditions: none.
Postcondition: product is the product of
multiplicand and multiplier.
divide
Inputs: dividend and divisor, both integers.
Outputs: quotient and remainder, both
integers.
Precondition: divisor is not 0.
Postconditions: dividend is the sum of remainder
and the product of quotient and divisor. The
magnitude of remainder is less than the magnitude of
divisor. If remainder is not 0, its sign
is the same as the sign of dividend.
quotient
Inputs: dividend and divisor, both integers.
Output: result, an integer.
Precondition: divisor is not 0.
Postcondition: result is the quotient resulting from the
division of dividend by divisor. (In other
words, it is the first output when divide is applied to
dividend and divisor.)
remainder
Inputs: dividend and divisor, both integers.
Output: result, an integer.
Precondition: divisor is not 0.
Postcondition: result is the remainder resulting from the
division of dividend by divisor. (In other
words, it is the second output when divide is applied to
dividend and divisor.)
modulo
Inputs: moduland and modulus, both integers.
Output: result, an integer.
Precondition: modulus is not 0.
Postconditions: The difference between moduland and
result is an exact multiple of modulus.
The magnitude of result is less than the
magnitude of modulus. If result is not 0,
its sign is the same as the sign of modulus.
raise
Inputs: base, an integer, and exponent, a natural
number.
Output: power, an integer.
Preconditions: none.
Postconditions: power is the result of raising
base to the power of exponent.
If both base and exponent are 0,
power is 1.
The next five operations are special cases of the preceding ones that occur frequently enough to be treated separately:
successor
Input: operand, an integer.
Output: result, an integer.
Preconditions: none.
Postcondition: result is the integer immediately following
operand. (In other words, result is the sum of
operand and 1.)
predecessor
Input: operand, an integer.
Output: result, an integer.
Preconditions: none.
Postcondition: operand is the integer immediately following
result. (In other words, oeprand is the sum of
result and 1.)
twice
Input: operand, an integer.
Output: result, an integer.
Preconditions: none.
Postcondition: result is the product of operand
and 2.
square
Input: operand, an integer.
Output: result, an integer.
Preconditions: none.
Postcondition: result is the result of raising
operand to the power 2.
cube
Input: operand, an integer.
Output: result, an integer.
Preconditions: none.
Postcondition: result is the result of raising
operand to the power 3.
Next, the comparison operations:
equal
Inputs: left-operand and right-operand, both
integers.
Output: result, a Boolean.
Preconditions: none.
Postcondition: result is true if the operands are the
same integer, false if they are different integers.
unequal
Inputs: left-operand and right-operand, both
integers.
Output: result, a Boolean.
Preconditions: none.
Postcondition: result is true if the operands are
different integers, false if they are the same integer.
less
Inputs: left-operand and right-operand, both
integers.
Output: result, a Boolean.
Preconditions: none.
Postcondition: result is true if
left-operand is less than right-operand,
false if it is greater than right-operand or if both
operands are the same integer.
less-or-equal
Inputs: left-operand and right-operand, both
integers.
Output: result, a Boolean.
Preconditions: none.
Postcondition: result is true if
left-operand is less than right-operand or if
both operands are the same integer, false if
left-operand is greater than right-operand.
greater
Inputs: left-operand and right-operand, both
integers.
Output: result, a Boolean.
Preconditions: none.
Postcondition: result is true if
left-operand is greater than right-operand,
false if it is less than right-operand or if both
operands are the same integer.
greater-or-equal
Inputs: left-operand and right-operand, both
integers.
Output: result, a Boolean.
Preconditions: none.
Postcondition: result is true if
left-operand is greater than right-operand or if
both operands are the same integer, false if
left-operand is less than right-operand.
major
Inputs: left-operand and right-operand, both
integers.
Output: result, an integer.
Preconditions: none.
Postcondition: result is the greater of the operands.
minor
Inputs: left-operand and right-operand, both
integers.
Output: result, an integer.
Preconditions: none.
Postcondition: result is the lesser of the operands.
Again, some special cases of the preceding predicates are worth defining:
zero
Input: operand, an integer.
Output: result, a Boolean.
Preconditions: none.
Postcondition: result is true if operand
is 0, false if it is any other integer.
negative
Input: operand, an integer.
Output: result, a Boolean.
Preconditions: none.
Postcondition: result is true if operand
is less than 0, false if it is greater or if it is
0.
positive
Input: operand, an integer.
Output: result, a Boolean.
Preconditions: none.
Postcondition: result is true if operand
is greater than 0, false if it is less or if it is
0.
Various tests for attributes of integers:
multiple
Inputs: candidate and unit, both integers.
Output: result, a Boolean.
Preconditions: none.
Postcondition: result is true if candidate
is an exact multiple of unit, false otherwise. (Note
that 0 is an exact multiple of every integer, including itself.)
even
Input: operand, an integer.
Output: result, a Boolean.
Preconditions: none.
Postcondition: result is true if operand
is an exact multiple of 2, false if it is not.
odd
Input: operand, an integer.
Output: result, a Boolean.
Preconditions: none.
Postcondition: result is false if operand
is an exact multiple of 2, true if it is not.
Finally, the input and output operations:
read
Input: source, a data source (e.g., a file, the keyboard, a
device).
Outputs: legend, an integer, and success, a
Boolean.
Preconditions: none.
Postcondition: Either some representation of an integer value has been
extracted from source and legend is that integer
value, or an input error of some kind has occurred and success
is false.
write
Inputs: target, a data sink (e.g., a file, a window, a
device), and scribend, an integer.
Outputs: none.
Preconditions: none.
Postcondition: A representation of scribend has been appended
to target.
Integer type are essentially
always represented as word-length data, and therefore constitute a finite,
fixed range of integers; integers outside of this range are not
representable. In effect, preconditions are added to all of the
operations, asserting that the inputs and outputs of the operation all lie
within the specified range. Since it is often difficult or impossible to
ascertain the truth of these preconditions before performing the
operations, this feature of standard Pascal is a common source of
programming errors.
In a subsequent handout, we'll develop a way of
avoiding this limitation, defining a different integer type that includes
arbitrarily large values. In the meantime, we'll try to live within the
confines of the standard Integer type, remembering to stay
within the permitted range.
In Standard Pascal, decimal numerals are used as names for values of the
Integer type. It also provides an alternative name,
MaxInt, for the largest value of the Integer data
type. It guarantees that all integers in the range from -MaxInt
to MaxInt, inclusive, are representable. (In many
implementations, such as HP Pascal, the integer -MaxInt - 1 is
also representable, but the standard does not guarantee this.)
Of the operations listed above, standard Pascal provides about half:
-)
Abs function)
+ operator)
-)
* operator)
div operator)
Succ function)
Pred function)
Sqr function)
=)
<>)
<)
<=)
>)
>=)
Odd function)
Write procedure)
mod
operator), but adds the precondition that modulus be positive;
and it provides a version of read that crashes when the input is not
to its liking, rather than setting a success flag.
Standard Pascal also provides an identity operation on integers -- that is,
a function (Ord) that takes any integer as argument and returns
the same integer.
{ Even in implementations, such as HP Pascal, where -MaxInt - 1 is a
representable integer, it usually has to receive exceptional treatment in
arithmetic operations, since its negative is not representable. (This
implies, for instance, that one cannot take its absolute value, divide it
by -1, etc.) In these procedures, I've dealt with the problem by writing
assertions that filter out this value whenever it would interfere with
the usual algorithm.
The identifier MinInt is predefined (as a non-standard extension to the
language) in HP Pascal. }
procedure Divide (Dividend: Integer; Divisor: Integer;
var Quotient: Integer; var Remainder: Integer);
begin
{ Assert (Divisor <> 0); }
{ Assert ((Dividend <> MinInt) and (Divisor <> MinInt); }
Quotient := Dividend div Divisor;
Remainder := Dividend - (Quotient * Divisor)
end;
function Remainder (Dividend: Integer; Divisor: Integer): Integer;
var
UnsignedResult: Integer;
begin
{ Assert (Divisor <> 0); }
{ Assert ((Dividend <> MinInt) and (Divisor <> MinInt)); }
UnsignedResult := Abs (Dividend) mod Abs (Divisor);
if Dividend < 0 then
Remainder := -UnsignedResult
else
Remainder := UnsignedResult
end;
function Modulo (Moduland: Integer; Modulus: Integer): Integer;
var
UnsignedResult: Integer;
begin
{ Assert (Modulus <> 0); }
{ Assert ((Moduland <> MinInt) and (Modulus <> MinInt)); }
UnsignedResult := Moduland mod Abs (Modulus);
if (Modulus < 0) and (UnsignedResult <> 0) then
Modulo := UnsignedResult + Modulus
else
Modulo := UnsignedResult
end;
{ The Raise function presupposes that the result of the exponentiation
operation is representable as an integer, but does not confirm this
presupposition.
The algorithm used in this function begins with three arithmetic facts:
k^0 = 1
k^(2n) = (k^n)^2
k^(2n + 1) = k * (k^n)^2
Thus, if Exponent is 0, the function can immediately return 1; if it is
greater than 0 and even, the function can raise the same base to a
power half as large, then square the result; if it is greater than 0
and odd, the function can perform the same squaring operation, then
multiply the result by the base.
The recursion must terminate, since Exponent is initially non-negative
and is strictly smaller on each recursive call, while never becoming
negative. It must therefore eventually reach 0, at which point the
recursion stops. }
function Raise (Base: Integer; Exponent: Integer): Integer;
var
Square: Integer;
begin
{ Assert (0 <= Exponent); }
if Exponent = 0 then
Raise := 1
else begin
Square := Sqr (Raise (Base, Exponent div 2));
if Odd (Exponent) then
Raise := Square * Base
else
Raise := Square
end
end;
{ The Twice function presupposes that the result of the doubling
operation is representable as an integer. }
function Twice (Operand: Integer): Integer;
begin
{ Assert ((MinInt div 2 <= Operand) and (Operand <= MaxInt div 2)); }
Twice := 2 * Operand
end;
{ The Cube function presupposes that its result is representable as an
integer. }
function Cube (Operand: Integer): Integer;
const
IntegerCubeRootOfMinInt = -1290;
IntegerCubeRootOfMaxInt = 1290;
begin
{ Assert ((IntegerCubeRootOfMinInt <= Operand) and
(Operand <= IntegerCubeRootOfMaxInt)); }
Cube := Operand * Operand * Operand
end;
function Major (LeftOperand, RightOperand: Integer): Integer;
begin
if LeftOperand < RightOperand then
Major := RightOperand
else
Major := LeftOperand
end;
function Minor (LeftOperand, RightOperand: Integer): Integer;
begin
if LeftOperand < RightOperand then
Minor := LeftOperand
else
Minor := RightOperand
end;
function Zero (Operand: Integer): Boolean;
begin
Zero := (Operand = 0)
end;
function Negative (Operand: Integer): Boolean;
begin
Negative := (Operand < 0)
end;
function Positive (Operand: Integer): Boolean;
begin
Positive := (0 < Operand)
end;
function Multiple (Candidate: Integer; Unit: Integer): Boolean;
begin
{ Assert (Unit <> MinInt); }
if Unit = 0 then
Multiple := (Candidate = 0)
else
Multiple := (Candidate mod Abs (Unit) = 0)
end;
function Even (Operand: Integer): Boolean;
begin
Even := not Odd (Operand)
end;
This document is available on the World Wide Web as
http://www.math.grin.edu/~stone/courses/fundamentals/integers.html