0.10101 + 0.11111 = 1.10100,
which, because no precision was lost, equals the exact answer.
Now, consider the decimal addition
5.25 + 0.96875 = 6.21875,
which, in binary, becomes
0.101010 × 2 3 + 0.111110 × 2 0 .
To carry out the binary addition, because the numbers have
different exponents, the mantissa of the smaller number is
shifted to produce a common exponent. If some of the bits representing the smaller number are lost, inaccuracy may result.
For example, in this case,
0.101010 × 2 3 + 0.000111 × 2 3 = 0.110001 × 2 3
= 6.125 (decimal).
The precision available on a computer is generally adequate
to avoid significant round-off error. Nonetheless, it is a potential problem to keep in mind, especially in long calculations or
in those such as a series sum where the answer is the difference
between large numbers.
A.8.4 A few pitfalls
Difficulties often can be avoided by considering how various
statements in the program will be executed. This is especially
the case when using compilers that provide little error checking
and few helpful warning and error messages. The computer,
following its explicit rules, may yield results differing from
those expected. The foibles here are for Fortran, but similar
ones often appear in other computer languages.
• Statement execution. Problems often stem from the distinction between integers and floating point numbers. For example,
if I and J are integer variables,
J = 5
I = 1/J
yields zero, because integer division yields an integer. This
problem is not cured by setting the result equal to a floating
point variable, or performing a floating point operation on the
integer result:
X = 1/J
Z = 1.0 * (1/J)
yield zero, because division is done as an integer operation, and
the result (0) is converted to floating point (0.0). On the other
hand, most compilers give 0.2 as the result of
X = 1.0/J,
although a conservative policy is to explicitly convert the integer
to floating point
X = 1.0/FLOAT(J).
A second class of problems can result from the order in
which operations are performed. For example, it may be
unclear whether
−1.0**2
should be interpreted as (−1.0) 2 = 1.0 or −(1.0)
2
= −1.0.
Although the computer language rules are explicit, it may be
wise to use parentheses, e.g.,
(−1.0)**2
to ensure that operations are carried out as desired. The
additional parentheses can also make the program more
comprehensible.
• Subroutines. Subroutines are heavily used in writing scientific programs. As a result, problems can result while using
computer languages like Fortran in which what appear to be
arguments passed to a subroutine are actually the locations in
memory of these arguments.
A common error is exemplified by the following program
CALL SUB(1.0)
X = 1.0
WRITE (6,*) ’X = ’, X
STOP
END
SUBROUTINE SUB(Y)
Y = 5.0
RETURN
END
which, when executed, yields “X = 5.0.” Because Y, a parameter in the subroutine definition, was set equal to 5.0, the
value of the corresponding parameter in the subroutine call,
“1.0” has been redefined as 5.0. This situation, which sometimes underlies inexplicable behavior by programs, can be
avoided by not passing numerical values of an argument explicitly to a subroutine if the argument will be redefined. For
example, had the first statements been
Z = 1.0
CALL SUB(Z)
the variable Z would equal 5.0, but “1.0” would not be
affected.
Other errors occur when either the type or number of arguments in a call to a subroutine do not match those in its definition. For example, calling a subroutine with an integer variable
may yield unexpected results if the definition is in terms of a
real variable.
A.8 Scientific programming 471
which, because no precision was lost, equals the exact answer.
Now, consider the decimal addition
5.25 + 0.96875 = 6.21875,
which, in binary, becomes
0.101010 × 2 3 + 0.111110 × 2 0 .
To carry out the binary addition, because the numbers have
different exponents, the mantissa of the smaller number is
shifted to produce a common exponent. If some of the bits representing the smaller number are lost, inaccuracy may result.
For example, in this case,
0.101010 × 2 3 + 0.000111 × 2 3 = 0.110001 × 2 3
= 6.125 (decimal).
The precision available on a computer is generally adequate
to avoid significant round-off error. Nonetheless, it is a potential problem to keep in mind, especially in long calculations or
in those such as a series sum where the answer is the difference
between large numbers.
A.8.4 A few pitfalls
Difficulties often can be avoided by considering how various
statements in the program will be executed. This is especially
the case when using compilers that provide little error checking
and few helpful warning and error messages. The computer,
following its explicit rules, may yield results differing from
those expected. The foibles here are for Fortran, but similar
ones often appear in other computer languages.
• Statement execution. Problems often stem from the distinction between integers and floating point numbers. For example,
if I and J are integer variables,
J = 5
I = 1/J
yields zero, because integer division yields an integer. This
problem is not cured by setting the result equal to a floating
point variable, or performing a floating point operation on the
integer result:
X = 1/J
Z = 1.0 * (1/J)
yield zero, because division is done as an integer operation, and
the result (0) is converted to floating point (0.0). On the other
hand, most compilers give 0.2 as the result of
X = 1.0/J,
although a conservative policy is to explicitly convert the integer
to floating point
X = 1.0/FLOAT(J).
A second class of problems can result from the order in
which operations are performed. For example, it may be
unclear whether
−1.0**2
should be interpreted as (−1.0) 2 = 1.0 or −(1.0)
2
= −1.0.
Although the computer language rules are explicit, it may be
wise to use parentheses, e.g.,
(−1.0)**2
to ensure that operations are carried out as desired. The
additional parentheses can also make the program more
comprehensible.
• Subroutines. Subroutines are heavily used in writing scientific programs. As a result, problems can result while using
computer languages like Fortran in which what appear to be
arguments passed to a subroutine are actually the locations in
memory of these arguments.
A common error is exemplified by the following program
CALL SUB(1.0)
X = 1.0
WRITE (6,*) ’X = ’, X
STOP
END
SUBROUTINE SUB(Y)
Y = 5.0
RETURN
END
which, when executed, yields “X = 5.0.” Because Y, a parameter in the subroutine definition, was set equal to 5.0, the
value of the corresponding parameter in the subroutine call,
“1.0” has been redefined as 5.0. This situation, which sometimes underlies inexplicable behavior by programs, can be
avoided by not passing numerical values of an argument explicitly to a subroutine if the argument will be redefined. For
example, had the first statements been
Z = 1.0
CALL SUB(Z)
the variable Z would equal 5.0, but “1.0” would not be
affected.
Other errors occur when either the type or number of arguments in a call to a subroutine do not match those in its definition. For example, calling a subroutine with an integer variable
may yield unexpected results if the definition is in terms of a
real variable.
A.8 Scientific programming 471
