472 Appendix
• Arrays. Scientific computing often involves dealing with
arrays, groups of data addressed by their indices. For example,
a seismogram giving a single component (e.g., vertical) of
ground motion can be written as an array (U(1), U(2) . . .) of
displacement versus time. Similarly, a seismogram giving all
three (vertical, north–south, east–west) ground motion components can be written as a two-dimensional array
U(1, 1), U(1, 2), U(1, 3), U(1, 4) . . .
U(2, 1), U(2, 2), U(2, 3), U(2, 4) . . .
U(3, 1), U(3, 2), U(3, 3), U(3, 4) . . .
whose first index gives the component, and second index indicates the time.
Arrays are defined initially by statements giving their dimensions, i.e.,
DIMENSION A(N, M)
or
REAL A(N, M).
Typically, the computer selects a memory location for the first
element in A and reserves N × M successive locations. Similarly, N × M × R locations are reserved for a three-dimensional
array dimensioned (N, M, R). In Fortran, regardless of the
number of dimensions, an array is stored as one-dimensional
with the first index varying the most rapidly, then the second,
and so on. In other words, if A is dimensioned (2, 3), the storage order is
A(1,1), A(2,1), A(1, 2), A(2,2), A(1,3), A(2,3).
For two-dimensional arrays, this can be thought of as storing
the array by columns. An individual array element is found by
calculating its location relative to that of the first element.
Thus, for an array dimensioned (N, M), with element (1, 1) at
location 1, element (I, J) is found at location
1 + (I − 1) + (J − 1) × N.
Several computational difficulties can arise in dealing with
arrays. A common set of errors involve being “off by one,”
either by starting or ending on the wrong element. This is
especially easy because some computer languages (e.g., Fortran) start with the first element in an array being “1,” whereas
others (e.g., “C”) start with the first array element as “0.” Thus
one needs to make sure that the array elements correspond
to the expected variable values, such as seismic record times.
Often, when an array index is computed by the program, an
error yields an index outside the bounds dimensioned for the
array. Because many compilers do not check for such errors
unless specifically requested, a statement like
A(9) = 4.0
will usually be executed even for an array dimensioned
DIMENSION A(5).
Typically, the computer places 4.0 in whatever is 8 locations in
memory beyond A(1). This location may contain some other
variable, or a portion of the program itself. Often the program
continues until it requires the contents of the overwritten location, at which point several things may occur. At best, the program “crashes”; at worst, it continues the calculation with
erroneous values that propagate. Array element out-of-bounds
problems are among the most common and most frustrating
difficulties in scientific programming. When a compiler provides array bounds checking, it is worth using.
The nature of array storage can also lead to inefficient programs. On many computers, data which are actually on disk
can be treated as resident in memory, and are automatically
“swapped” into physical memory when needed. For efficiency,
large adjacent regions of the disk are often swapped into physical memory together. Efficient programs minimize swapping
by making the most possible use of data that reside in physical memory. By contrast, inefficient programs can produce
“thrashing,” a situation in which much of the computer’s time
is spent swapping rather than computing.
For example, consider 4
DIMENSION A(1000, 1000)
DO 10 I = 1,1000
DO 10 J = 1,1000
10 A(I, J) = I + J
Because the elements of A are stored in column order, A(1, 1)
and A(1, 2) are a thousand locations apart. It would be more
efficient to reverse the loops
10 A(J, I) = I + J
so that adjacent locations (A(1, 1), A(2, 1) . . .) were used
successively.
• Uninitialized variables. Problems frequently result from
uninitialized variables: those used in calculation without their
values being set. A common example, summing an array
DO 10 I = 1, N
10 SUM = SUM + A(I)
can give strange results unless the compiler initializes SUM
as zero. Because this is not always the case, it is thus wise to
explicitly initialize, e.g.,
SUM = 0.0
before executing the loop. Proper initialization also helps to
ensure that programs do not give different results on different
computers.
• The computer may be wrong. Although most problems
result from programming errors, a very small fraction of the
time the error may be the computer’s. Compilers have been
known to contain “bugs” in common routines such as square
root, tangent, or complex arithmetic. This tempting explanation for the failure of a long and intricate program can generally be rejected unless a test program that carries out only the
suspect operation yields the wrong answer.
A.8.5 Some philosophical points
To close our discussion, a few general thoughts are worth
considering. Historically, computers were considered a scarce
and valuable resource. Currently, as computer power increases
and costs fall, it is increasingly practical to carry out investi4 Hatton (1983c).
• Arrays. Scientific computing often involves dealing with
arrays, groups of data addressed by their indices. For example,
a seismogram giving a single component (e.g., vertical) of
ground motion can be written as an array (U(1), U(2) . . .) of
displacement versus time. Similarly, a seismogram giving all
three (vertical, north–south, east–west) ground motion components can be written as a two-dimensional array
U(1, 1), U(1, 2), U(1, 3), U(1, 4) . . .
U(2, 1), U(2, 2), U(2, 3), U(2, 4) . . .
U(3, 1), U(3, 2), U(3, 3), U(3, 4) . . .
whose first index gives the component, and second index indicates the time.
Arrays are defined initially by statements giving their dimensions, i.e.,
DIMENSION A(N, M)
or
REAL A(N, M).
Typically, the computer selects a memory location for the first
element in A and reserves N × M successive locations. Similarly, N × M × R locations are reserved for a three-dimensional
array dimensioned (N, M, R). In Fortran, regardless of the
number of dimensions, an array is stored as one-dimensional
with the first index varying the most rapidly, then the second,
and so on. In other words, if A is dimensioned (2, 3), the storage order is
A(1,1), A(2,1), A(1, 2), A(2,2), A(1,3), A(2,3).
For two-dimensional arrays, this can be thought of as storing
the array by columns. An individual array element is found by
calculating its location relative to that of the first element.
Thus, for an array dimensioned (N, M), with element (1, 1) at
location 1, element (I, J) is found at location
1 + (I − 1) + (J − 1) × N.
Several computational difficulties can arise in dealing with
arrays. A common set of errors involve being “off by one,”
either by starting or ending on the wrong element. This is
especially easy because some computer languages (e.g., Fortran) start with the first element in an array being “1,” whereas
others (e.g., “C”) start with the first array element as “0.” Thus
one needs to make sure that the array elements correspond
to the expected variable values, such as seismic record times.
Often, when an array index is computed by the program, an
error yields an index outside the bounds dimensioned for the
array. Because many compilers do not check for such errors
unless specifically requested, a statement like
A(9) = 4.0
will usually be executed even for an array dimensioned
DIMENSION A(5).
Typically, the computer places 4.0 in whatever is 8 locations in
memory beyond A(1). This location may contain some other
variable, or a portion of the program itself. Often the program
continues until it requires the contents of the overwritten location, at which point several things may occur. At best, the program “crashes”; at worst, it continues the calculation with
erroneous values that propagate. Array element out-of-bounds
problems are among the most common and most frustrating
difficulties in scientific programming. When a compiler provides array bounds checking, it is worth using.
The nature of array storage can also lead to inefficient programs. On many computers, data which are actually on disk
can be treated as resident in memory, and are automatically
“swapped” into physical memory when needed. For efficiency,
large adjacent regions of the disk are often swapped into physical memory together. Efficient programs minimize swapping
by making the most possible use of data that reside in physical memory. By contrast, inefficient programs can produce
“thrashing,” a situation in which much of the computer’s time
is spent swapping rather than computing.
For example, consider 4
DIMENSION A(1000, 1000)
DO 10 I = 1,1000
DO 10 J = 1,1000
10 A(I, J) = I + J
Because the elements of A are stored in column order, A(1, 1)
and A(1, 2) are a thousand locations apart. It would be more
efficient to reverse the loops
10 A(J, I) = I + J
so that adjacent locations (A(1, 1), A(2, 1) . . .) were used
successively.
• Uninitialized variables. Problems frequently result from
uninitialized variables: those used in calculation without their
values being set. A common example, summing an array
DO 10 I = 1, N
10 SUM = SUM + A(I)
can give strange results unless the compiler initializes SUM
as zero. Because this is not always the case, it is thus wise to
explicitly initialize, e.g.,
SUM = 0.0
before executing the loop. Proper initialization also helps to
ensure that programs do not give different results on different
computers.
• The computer may be wrong. Although most problems
result from programming errors, a very small fraction of the
time the error may be the computer’s. Compilers have been
known to contain “bugs” in common routines such as square
root, tangent, or complex arithmetic. This tempting explanation for the failure of a long and intricate program can generally be rejected unless a test program that carries out only the
suspect operation yields the wrong answer.
A.8.5 Some philosophical points
To close our discussion, a few general thoughts are worth
considering. Historically, computers were considered a scarce
and valuable resource. Currently, as computer power increases
and costs fall, it is increasingly practical to carry out investi4 Hatton (1983c).
