4-10 A NOTE ON INTEGERS ************************ Signed and unsigned integers ---------------------------- FORTRAN integers are SIGNED INTEGERS, half the possible bit patterns are used to represent positive values (and 0), the other half is used for the negative values. A table showing the bit patterns that can be constructed with 4 bits, and the integers that are associated with them: Bit Signed Unsigned Pattern integer integer ------------ -------- -------- 1111 -1 15 1110 -2 14 1101 -3 13 1100 -4 12 1011 -5 11 1010 -6 10 1001 -7 9 1000 -8 8 0111 7 7 0110 6 6 0101 5 5 0100 4 4 0011 3 3 0010 2 2 0001 1 1 0000 0 0 Of course no FORTRAN integer type is really made of half a byte, but that is enough to illustrate the general principles. CPU hardware adds integers bit by bit, from the Least Significant Bit (LSB) to the Most Significant Bit (MSB), taking into account the value carried from the previous binary bit addition. We will call the correspondence between the possible bit patterns and unsigned integers the natural representation. Natural binary representation of positive integers -------------------------------------------------- The binary representation of a positive integer N is: N = A0*1 + A1*2 + A2*4 + A3*8 + A4*16 + ... Ai = {0,1} The Ai coefficients are the reminders on repeated divisions by 2: A0 = N - (2 * (N / 2)) = MOD(N, 2) N = N / 2 A1 = N - (2 * (N / 2)) = MOD(N, 2) ...................... The resulting binary number can be written: ... A4 A3 A2 A1 A0 Other binary representations of positive integers ------------------------------------------------- Two more representation methods were invented for positive integers, offset binary and the binary coded decimal family (BCD). These methods are better understood in the context of extending the representation to include negative integers, and so will be discussed in the next paragraph. Number systems that include negative & positive integers -------------------------------------------------------- Extending the 'natural' binary representation of positive integers to negative integers can be done in at least 3 different schemes: sign-magnitude, one's complement and two's complement. SIGN-MAGNITUDE The most significant bit (MSB) is reserved to the sign, 0 is positive, 1 is negative. All other bits are used to store the magnitude in the natural representation. Addition and subtraction are complicated. There are two representations for zero! ONE'S COMPLEMENT Positive integers are like in the natural representation, negative numbers are obtained by complementing each bit of the corresponding positive number (i.e. the absolute value) There are two representations for zero! Bitwise addition of N and -N gives -0. Positive integers still have MSB = 0, and negative integers have MSB=1. TWO'S COMPLEMENT Like one's complement, but negative numbers are having 1 added after complementation. Bitwise addition of N and -N gives 0 if you ignore the carry out of the MSB. Positive integers still have MSB = 0, and negative integers have MSB=1. Only one representation for zero. Negating an integer is always done using the operation of bitwise complementation, i.e. reversing the value of each bit: 0 ---> 1 1 ---> 0 The only integer representation used in modern computers is two's complement (One's complement was used in the classic CDC machines). Two's complement allows the same CPU circuitry to perform addition and subtraction, subtraction is just addition of the negated number. See Appendix A for a method to diagnose the type of integer arithmetic on your computer. Two other number systems that are not extensions of the 'natural' binary representation of positive integers, i.e. they give other values to the positive integers are offset binary and BCD. OFFSET BINARY Rarely used method, the value assigned to a bit pattern is the 'natural value' minus the bit pattern 100...0. Forms an 'ascending' series from the negative to the positive numbers. Equals two's complement but with MSB complemented. Only one representation for zero. BCD A family of coding systems, all of them generated by taking the decimal representation and coding each decimal digit (and the sign) with 4 bits. The following table illustrates the representation methods with 4 bits bit patterns: Sign- One's two's Offset Integer magnitude complement complement binary ------- --------- ---------- ---------- ------ +7 0111 0111 0111 1111 +6 0110 0110 0110 1110 +5 0101 0101 0101 1101 +4 0100 0100 0100 1100 +3 0011 0011 0011 1011 +2 0010 0010 0010 1010 +1 0001 0001 0001 1001 0 0000 0000 0000 1000 -1 1001 1110 1111 0111 -2 1010 1101 1110 0110 -3 1011 1100 1101 0101 -4 1100 1011 1100 0100 -5 1101 1010 1011 0011 -6 1110 1001 1010 0010 -7 1111 1000 1001 0001 -8 ---- ---- 1000 0000 (-0) 1000 1111 ---- ---- The following table shows the three common BCD coding systems (BCD 8421 is known as just BCD): Pattern BCD 8421 Excess-3 BCD 2421 -------- -------- -------- -------- 1111 9 1110 8 1101 7 1100 9 6 1011 8 5 1010 7 1001 9 6 1000 8 5 0111 7 4 0110 6 3 0101 5 2 0100 4 1 4 0011 3 0 3 0010 2 2 0001 1 1 0000 0 0 BCD arithmetic on strings is really a kind of multiple precision arithmetic (bignums). Old computers implemented BCD in hardware, but it is inefficient compared with integers and reals. Excess-3 and BCD 2421 were devised so that bitwise complementation will give the 9's complement (the radix 2 analog of 1's complement) of the decimal number, making hardware implementation require more simple circuitry. Using integers in place of floating-point numbers ------------------------------------------------- Integers are sometimes called fixed-point numbers because they can be viewed as floats with radix point after the least significant digit, and zero fractional digits: 1 1.000... 10 10.000... 100 100.000... This strange representation hints at a possible use of integers as a replacement for floats. Instead of a float X with p digits decimal mantissa, we can use an integer N: N = INT(X * (10**p)) (Float to integer transformation) X = N / (10**p) (Recovering the float from the integer) Addition is simple, you just add the integers. X1 + X2 = N1 / (10**p) + N2 / (10**p) = (N1 + N2) / (10**p) Multiplication is more tricky: X1 * X2 = [N1 / (10**p)] * [N2 / (10**p)] = (N1 * N2) / [(10**p)**2] = [(N1 * N2) / (10**p)] / (10**p) Division by an extra (10**p) factor is needed to get the right answer. A common INTEGER type is the INTEGER*4 which has range: [-2,147,483,648 : 2,147,483,647] that means that with a 3 digits decimal mantissa you will get only the range: [-2,147,483.648 : 2,147,483.647] The advantage of using integers instead of floats is that roundoff errors are eliminated on addition and subtraction (problematic operations with floats). Radix conversions between the internal binary representation and external decimal are also errorless. Integer operations takes about the same CPU time as the corresponding float operations. Unsigned integers ----------------- If you work with quantities that are always positive, and even intermediary and temporary results are positive, half of the integer range is 'wasted'. However, unsigned integers are awkward to emulate with signed integer arithmetic(?). You may get the output of a measuring device in a file containing unsigned integers, or get a file created by a program written in a language that supports unsigned integers, e.g. C. Comparing unsigned integers --------------------------- Unsigned integers can be compared with a small routine that uses the ordinary FORTRAN comparison operators: LOGICAL FUNCTION UGE(I, J) C ------------------------------------------------------------------ INTEGER I, J C ------------------------------------------------------------------ IF (I .GE. 0) THEN IF (J .LT. 0) THEN UGE = .FALSE. RETURN ENDIF ELSE IF (J .GE. 0) THEN UGE = .TRUE. RETURN ENDIF ENDIF C ------------------------------------------------------------------ IF (I .GE. J) THEN UGE = .TRUE. ELSE UGE = .FALSE. ENDIF C ------------------------------------------------------------------ RETURN END This little monster can be further optimized ...Return to contents page