Preliminaries: in Fortran 77, Numerical Recipes in Fortran 90, and Numerical Recipes in C Are
Preliminaries: in Fortran 77, Numerical Recipes in Fortran 90, and Numerical Recipes in C Are
Preliminaries
1.0 Introduction
This book, like its sibling versions in other computer languages, is supposed to
teach you methods of numerical computing that are practical, efficient, and (insofar
as possible) elegant. We presume throughout this book that you, the reader, have
particular tasks that you want to get done. We view our job as educating you on
how to proceed. Occasionally we may try to reroute you briefly onto a particularly
beautiful side road; but by and large, we will guide you along main highways that
lead to practical destinations.
Throughout this book, you will find us fearlessly editorializing, telling you
what you should and shouldnt do. This prescriptive tone results from a conscious
decision on our part, and we hope that you will not find it irritating. We do not
claim that our advice is infallible! Rather, we are reacting against a tendency, in
the textbook literature of computation, to discuss every possible method that has
ever been invented, without ever offering a practical judgment on relative merit. We
do, therefore, offer you our practical judgments whenever we can. As you gain
experience, you will form your own opinion of how reliable our advice is.
We presume that you are able to read computer programs in C++, that being
the language of this version of Numerical Recipes. The books Numerical Recipes
in Fortran 77, Numerical Recipes in Fortran 90, and Numerical Recipes in C are
separately available, if you prefer to program in one of those languages. Earlier
editions of Numerical Recipes in Pascal and Numerical Recipes Routines and Ex-
amples in BASIC are also available; while not containing the additional material of
the Second Edition versions, these versions are perfectly serviceable if Pascal or
BASIC is your language of choice.
When we include programs in the text, they look like this:
#include <cmath>
#include "nr.h"
using namespace std;
1
2 Chapter 1. Preliminaries
DP am,as,c,t,t2,xtra;
Note our convention of handling all errors and exceptional cases with a statement
like nrerror("some error message");. The function nrerror() is part of a
small file of utility programs, nrutil.h, listed in Appendix B at the back of the
book. This Appendix includes a number of other utilities that we will describe
later in this chapter. Function nrerror() prints the indicated error message to your
stderr device (usually your terminal screen), and then invokes the function exit(),
which terminates execution. You can modify nrerror() so that it does anything
else that will halt execution. For example, you can have it pause for input from the
keyboard, and then manually interrupt execution. In some applications, you will
want to modify nrerror() to do more sophisticated error handling, for example to
transfer control somewhere else by throwing a C++ exception.
We will have more to say about the C++ programming language, its conventions
and style, in 1.1 and 1.2.
This section is for people who want to jump right in. Well compute the mean
and variance of the Julian Day numbers of the first 20 full moons after January 1900.
(Now theres a useful pair of quantities!)
First, locate the important files nrtypes.h, nrutil.h, and nr.h, as listed
in Appendices A and B. These contain the definitions of the various types used
by our routines, the vector and matrix classes we use, various utility functions,
and the function declarations for all the Recipe functions. (Actually, nrtypes.h
includes by default the file nrtypes nr.h, and nrutil.h includes by default the
file nrutil nr.h. This setup is to allow you to change the defaults easily, as
will be discussed in 1.3.)
Second, create this main program file:
#include <iostream>
#include <iomanip>
#include "nr.h"
using namespace std;
int main(void)
{
1.0 Introduction 3
for (i=0;i<NTOT;i++) {
NR::flmoon(i,nph,jd,frac);
data[i]=jd;
}
NR::avevar(data,ave,vrnce);
cout << "Average = " << setw(12) << ave;
cout << " Variance = " << setw(13) << vrnce << endl;
return 0;
}
Third, compile the main program file, and also the files flmoon.cpp and
avevar.cpp. Link the resulting object files.
Fourth, run the resulting executable file. Typical output is:
The files nrtypes.h, nrutil.h, and nr.h and the concepts behind them will
be discussed in detail in 1.2 and 1.3.
Our goal is that the programs in this book be as portable as possible, across
different platforms (models of computer), across different operating systems, and
across different C++ compilers. C++ was designed with this type of portability in
mind. Nevertheless, we have found that there is no substitute for actually checking
all programs on a variety of compilers, in the process uncovering differences in
library structure or contents, and even occasional differences in allowed syntax. As
surrogates for the large number of hardware and software configurations, we have
tested all the programs in this book on the combinations of machines, operating
systems, and compilers shown on the accompanying table. More generally, the
programs should run without modification on any compiler that implements the
ANSI/ISO C++ standard, as described for example in Stroustrups excellent book [1].
In validating the programs, we have taken the program source code directly
from the machine-readable form of the books manuscript, to decrease the chance
of propagating typographical errors. Driver or demonstration programs that we
used as part of our validations are available separately as the Numerical Recipes
Example Book (C++), as well as in machine-readable form. If you plan to use
more than a few of the programs in this book, then you may find it useful to obtain
4 Chapter 1. Preliminaries
the machine-readable software distribution, which includes both the Recipes and
the demonstration programs.
Of course we would be foolish to claim that there are no bugs in our programs,
and we do not make such a claim. We have been very careful, and have benefitted
from the experience of the many readers who have written to us. If you find a
new bug, please document it and tell us! You can find contact information at
http://www.nr.com.
Wirth, N. 1983, Programming in Modula-2, 3rd ed. (New York: Springer-Verlag). [4]
Stroustrup, B. 1997, The C++ Programming Language, 3rd ed. (Reading, MA: Addison-Wesley).
[5]
Meeus, J. 1982, Astronomical Formulae for Calculators, 2nd ed., revised and enlarged (Rich-
mond, VA: Willmann-Bell). [6]
Hatcher, D.A. 1984, Quarterly Journal of the Royal Astronomical Society, vol. 25, pp. 5355; see
also op. cit. 1985, vol. 26, pp. 151155, and 1986, vol. 27, pp. 506507. [7]
Double Precision
When the C version of this book first came out, the default precision used by
most scientists for most calculations was single precision. The reason was that
double precision imposed a significant overhead both in execution speed and in
memory requirements. (The fact that C automatically converts float variables to
double in many situations was another insult to scientific programmers!)
Nowadays, the execution speed overhead has essentially disappeared. There
are even machines where single precision is slower than double! And memory
conservation is often not the concern it used to be. Accordingly, the default precision
in this C++ edition is double precision. To make it easy to change to single precision
(or quadruple precision!) if you want to, we have not hard coded the type double in
the routines. Instead we have used the name DP, along with the typedef definition
This typedef occurs twice, once in nrtypes.h and once in nrutil.h. To change
the default precision to single, just change the definition in both places to
You will also have to change the values of certain accuracy parameters in
some routines. This is further described in Appendix C. For some of the Recipes
roundoff error is a particular concern. In those cases we will explicitly warn you
always to use double precision.
A function must also be defined somewhere (and defined once only). A function
definition consists of a function declaration plus the body of the function, the code
that actually does the work. For example,
If all your code is in one file, you can often arrange for each function definition
to precede the functions that call it. Since the definition includes the declaration,
the compiler can then check that a given function call invokes the function with the
correct argument types. However, this setup is feasible only for small programs. In
general, a C++ program consists of multiple source files that are separately compiled,
and the compiler cannot check the consistency of each function call without some
additional assistance. A simple and safe way to proceed is as follows [1]:
Every external function should have a single declaration in a header (.h)
file.
The source file with the definition (body) of the function should also
include the header file so that the compiler can check that the declaration
and the definition match.
Every source file that calls the function should include the same header file.
For the routines in this book, the header file containing all the declarations is nr.h,
listed in Appendix A. You should put the statement #include "nr.h" at the top of
every source file that invokes Numerical Recipes routines.
namespace NR {
void addint(Mat_O_DP &uf, Mat_I_DP &uc, Mat_O_DP &res);
...
void zroots(Vec_I_CPLX_DP &a, Vec_O_CPLX_DP &roots, const bool &polish);
}
(The types Mat_O_DP, etc. will be explained below.) The Numerical Recipes function
definitions have the following format:
#include "nr.h"
Note that the code includes nr.h (since that is where the namespace NR is defined).
Also, the function name must be prefixed with NR:: since NR is the scope in which
the function is being defined. Note that if the function happens to call another
Numerical Recipes function, no special declaration is required. Since all the Recipes
are in namespace NR, they are all within each others scope.
When you write a program that calls one of the Recipe functions, your program
must include nr.h. You then have three ways of invoking a particular Recipe:
Call the function with explicit scope resolution:
using NR::addint;
...
addint(uf, uc, res);
Const Correctness
Few topics in discussions about C++ evoke more heat than questions about the
keyword const. Here is our position: We are firm believers in using const wherever
possible, to achieve what is called const correctness. Many coding errors are
automatically trapped by the compiler if you have qualified identifiers that should
not change with const when they are declared. Also, using const makes your code
much more readable: When you see const in front of an argument to a routine, you
know immediately that the routine will not modify the object. Conversely, if const
is absent you should be able to count on the object being changed somewhere.
We are such strong const believers that we insert const even where many
people think it is redundant: If an argument is passed by value to a function, then
the function makes a copy of it. Even if this copy is modified by the function, the
original value is unchanged after the function exits. While this allows you to change,
with impunity, the values of arguments that have been passed by value, we believe
this usage is error-prone and hard to read. If your intention in passing something
by value is that it is an input variable only, then make it clear. So we declare a
function f (x) as, for example,
If in the function you want to use a local variable that is initialized to x but then gets
changed, define a new quantity dont use x. If you put const in the declaration,
the compiler will not let you get this wrong.
Some people think that using const on arguments makes your functions less
general. Quite the opposite! Calling a function that expects a const argument with
a non-const variable involves a trivial conversion. But trying to pass a const
quantity to a non-const argument is an error.
The final reason for using const is that it allows certain user-defined conversions
to be made. As we will see in 1.3, this is the key to writing our functions so that
you can use them transparently with any matrix/vector class library.
void someroutine(a,m,n)
double a[m][n]; /* ILLEGAL DECLARATION */
and emit code to evaluate the variable dimensions m and n (or any variable-dimension
expression) each time someroutine() is entered. Alas! the above fragment is
forbidden by the C++ language definition.
The natural way to deal with vectors and matrices in C++ is as classes. A
matrix object can contain not only the values of the matrix elements, but also
information on the matrix size. Moreover, the class can provide various overloaded
operators and functions to facilitate high-level programming. The C++ standard
already provides such a class for vectors (valarray), but not for matrices. Many
people have written their own class libraries for one- and two-dimensional arrays.
Most of these libraries are excellent in providing a suite of high-level constructs,
but are terribly inefficient in execution.
When we began preparing this C++ version of Numerical Recipes, our original
intention was to do it right: we would construct a class library with all the
necessary high-level constructs that would also be efficient. This turns out to be
a highly nontrivial task, especially since many compilers do not yet implement all
the features of the C++ standard that are necessary to make such code efficient.
Furthermore, we soon realized that this was exactly the wrong way to proceed. Why
should you, the reader, adopt our class library when you have probably already
invested a large effort in developing or using a different one? And what if you want
to use our routines in a code written with another class library?
So, instead, we have gone to completely the opposite extreme. The Recipe
functions in this book are written with vector and matrix classes called NRVec
and NRMat. These classes are defined in nrutil.h, and provide a minimal
1.2 Some C++ Conventions for Scientific Computing 21
implementation of vector and matrix operations. You can use our routines in either
of two ways:
Use our classes to handle vectors and matrices in all your programs. When
you include nr.h to make the Recipes available, it automatically includes
nrutil.h for you, so our vector and matrix classes will be accessible.
The only disadvantage of this is that our classes do not provide many
high-level constructs.
Use any vector and matrix classes you like. In 1.3, we will show you how
to set up a modified nrutil.h so that you will be able call our routines
transparently, even though ours are declared with the Numerical Recipes
classes NRVec and NRMat.
The machinery behind the NRVec and NRMat classes, and the way they can be
used with other class libraries, is quite complicated. Most readers will not want
to delve into this material. Accordingly, we defer its discussion to 1.3. All you
really need to know to be able to understand how vectors and matrices are used in
the Recipes is in the remainder of this section.
In 1.3 we describe the nitty-gritty of how our vector and matrix classes
are implemented, and how you can alternatively use any other matrix/vector library
instead. Many readers will find this tough going. Fortunately, it is possible to insulate
you almost entirely from the details of the matrix/vector library. In fact, if you peruse
the Recipe functions, you wont see the class names NRVec or NRMat appearing
anywhere. Instead, youll see names like Vec I DP and Mat O INT declaring
vectors and matrices. These are the identifiers you should actually use, and it is the
file nrtypes.h that encapsulates all these definitions in a set of typedef definitions.
The name we use for a typical type defined in nrtypes.h consists of three parts:
Vec or Mat for vector or matrix.
I, O, or IO for in, out, or in-out. These symbols happen to be based
on the corresponding Fortran 90 names intent in, intent out, and
intent inout, but the concepts are universal. They describe whether the
array being passed to the function supplies values to the function but does
22 Chapter 1. Preliminaries
not return values (intent in), returns values from the function but does not
supply any (intent out), or does both (intent inout).
the scalar type that is the intended default. For example, INT for int,
DP (double precision) for double, CPLX SP (complex single precision)
for complex<float>, and so on. Here is a complete list of all the types
we so denote:
BOOL bool
CHR char
UCHR unsigned char
INT int
UINT unsigned int
LNG long
ULNG unsigned long
SP float
DP double
CPLX SP complex<float>
CPLX DP complex<double>
ULNG p unsigned long *
DP p double *
FSTREAM p fstream *
The first line says that intent in vectors will be const double precision. The second
line says that intent out and intent inout vectors will not be const. The identifier
Vec DP is for vectors declared locally within a Recipe function, where the terms
in and out are not meaningful.
With these defined types, you can write programs completely without reference
to the implementation of the underlying vector and matrix classes. For example, you
might use statements like the following:
delete e;
}
A Few Wrinkles
We like to keep code compact, avoiding unnecessary spaces unless they add
immediate clarity. We usually dont put space around the assignment operator =.
For historical reasons, you will see us write y= -10.0; or y=(-10.0);, and y= *a;
or y=(*a);. This is just because there used to be some C compilers recognize the
(nonexistent) operator =- as being equivalent to the subtractive assignment operator
-=, and =* as being the same as the multiplicative assignment operator *=. We
hope that this quirkiness has disappeared by now, but we still have lingering habits.
We have the same viewpoint regarding unnecessary parentheses. You cant
write (or read) C++ effectively unless you memorize its operator precedence and
associativity rules. Please study the accompanying table while you brush your
teeth every night.
We never use the register storage class specifier. Good optimizing compilers
are quite sophisticated in making their own decisions about what to keep in registers,
and the best choices are sometimes rather counter-intuitive.
We like to use the C++ constructor for casting types: int(x) rather than the
C-style (int) x. Similarly, if a pointer to a function is passed as an argument, we
invoke the function with the C++ form func(x) rather than the C-style *func(x).
Some of our routines need to define a global vector or matrix to communicate
data between two routines, lets call them one() and two(), without using function
arguments. Typically the size of the data set needs to be set dynamically at runtime.
We handle this situation by making the global variable a pointer to the data, so that
function one would look something like this:
void one(...)
{
...
xvec_p=new Vec_DP(n); Allocate storage of size n.
Vec_DP &xvec= *xvec_p; Make alias to simplify subsequent coding.
...
delete xvec_p; Reclaim storage when done.
The reference variable xvec is defined only to make subsequent code easier to write
and read. Instead of writing (*xvec_p)[i] we can write xvec[i].
To use the global vector in function two, we use the following scheme:
void two(...)
{
...
Vec_DP &xvec= *xvec_p; Make alias to simplify subsequent coding.
...
* multiply left-to-right
/ divide
% remainder
+ add left-to-right
- subtract
| bitwise or left-to-right
|| logical or left-to-right
from C++ is perhaps the languages most galling continuing insult to the scientific
programmer. All good Fortran compilers recognize expressions like (a+b)**4
and produce in-line code, in this case with only one add and two multiplies. It is
typical for constant integer powers up to 12 to be thus recognized.
In nrutil.h we provide an inline templated function to handle squaring. Its
definition is
template<class T>
inline const T SQR(const T a) {return a*a;}
Youre on your own for higher powers. We also provide a collection of similar
functions for other simple operations: SQR, MAX, MIN, SIGN, and SWAP. These do the
obvious things (SIGN(a,b) returns the magnitude of a times the sign of b.)
Scientific programming in C++ may someday become a bed of roses; for now,
watch out for the thorns!
In this section we describe the details of the implementation of the vector and
matrix classes we use. We start with the default classes, and then describe how you
can instead use another matrix/vector library with our Recipe functions. Unless you
are an experienced C++ programmer, you will probably not want to read beyond the
first subsection. And remember: in practice you will actually never need to use the
names NRVec or NRMat. Instead, you use identifiers like Vec I DP as described
in the previous section.
The private variables are discussed in Appendix B. Lets look at the public interface.
The various constructors allow vectors to be declared as in the following examples:
NRVec<double> w(10);
int n = w.size(); Sets n to 10.
As we will discuss later, you can use any vector class you like with the
Numerical Recipes functions as long as it provides the basic functions above. (The
functions can be called something else; its the functionality that must be the same.)
In fact, you can get away with even less. As long as you provide the constructor for
a vector of length n, the subscript operator, and the size() function, most of the
Recipes will work. About ten will have to have some code replaced by explicit loops
to handle initializing to values or arrays. (The Numerical Recipes example routines
make more extensive use of the additional functions in the NRVec class.)
The matrix class NRMat is very similar to NRVec:
The only point to note is the return type of operator[]. If a is of type NRMat, we
want a[i] to point to row i of the matrix so that a[i][j] will be the (i, j) matrix
element. In our default class shown above, this means that a[i] must be of type T*.
However, it might be something else in a different, more complicated, class library.
Just as in the case of vectors, you will find out below how to use any matrix
class with the Recipes, as long as it provides the basic functions above. And if
all you supply is the constructor for an m n matrix, the subscript operator, and
the functions for the number of rows and columns, only about five routines will
need to be rewritten.
Only two of our routines use a three-dimensional array: rlft3 in 12.5 and
solvde in 17.3. This data structure is provided by the class NRMat3d in nrutil.h.
We have not made any special efforts for you to be able to use your own class instead
of the one we provide, but if necessary you could follow the same technique we
describe below for vectors and matrices.
The full implementation code for the NRVec and NRMat classes is in nrutil.h,
which is reproduced in Appendix B. Note that you can easily use the standard
library class valarray for vectors instead of NRVec: Simply replace the complete
declaration and implementation of the NRVec class with the following:
Alas, the standard library doesnt provide anything comparable to valarray for
matrices.
At this point we need to elaborate on what exactly const does for a non-simple
type such as a class that is an argument of a function. Basically it guarantees that
the object is not modified by the function. In other words, the data members are
unchanged. But note that if a data member is a pointer to some data, and the
data itself is not a member variable, then the data can be changed even though
the pointer cannot be.
Lets look at the implications of this for a function f that takes an NRVec
argument a. To avoid unnecessary copying, we always pass arrays by reference.
Consider the difference between declaring the argument of a function with and
without const:
The const version promises that f does not modify the data members of a. But
a statement like
a[i] = 4;
The first form returns a reference to an element of a modifiable vector, while the
second is for a nonmodifiable vector. The way these work in our example is that
if argument a of function f() above is declared as const reference, then when a
is dereferenced the second form of operator[] will be invoked. (It is the trailing
const in the declaration that promises not to modify the object being dereferenced,
and which must agree in constness with the argument.) Then the return type const
allows expressions like x = a[i] but forbids expressions like a[i] = 4 (a[i] is
not an lvalue). By contrast, if a is declared in f() without the const qualifier,
the first form of operator[] is invoked, which allows both kinds of statements
(a[i] can be an lvalue).1
This is the kind of trickery you have to resort to to get const to protect the data.
As judged from the large number of matrix/vector libraries that follow this scheme,
many people feel that the payoff is worthwhile.
However, we must also recognize the existence of an alternative implementation
of operator[], which is to stick with the basic C++ philosophy const protects
the container, not the contents. In this case you would want only one form of
operator[], namely
It would be invoked whether your vector was passed by const reference or not.
In both cases the size and pointer of the vector are unchanged, and element i is
returned as potentially modifiable.
While we do not use this second alternative in our default classes, since it is
nice to be able to use const to protect the contents, we must use it if we replace
our default classes with the classes described in the next subsection that allow you
to use your own favorite matrix/vector library instead of our defaults. The reason is
that with this alternative, all invocations of operator[] will enforce constness of
the object so as to allow the possibility of automatic conversions. (When an object
is passed to a function by reference, automatic type conversions can be made only if
the object is const.) Although giving up the extra const checking is regrettable,
1
Wait! you might object. The first form of operator[] doesnt actually modify the object, only the data.
Why cant I put a trailing const after it? Answer: because then the compiler could not distinguish between
the two forms. Overloaded functions and operators must be distinguishable by the argument types alone, not
by the return types.
1.3 Implementation of the Vector and Matrix Classes 29
this nevertheless turns out to be the best route to allowing transparent use of other
matrix/vector libraries, as we now explain.
template<class T>
class NRVec {
private:
MyVec<T> myvec;
MyVec<T> &myref;
public:
NRVec<T>() : myvec(), myref(myvec) {}
...
The private variables are only a MyVec and a reference to one. The private variable myvec
is used solely when creating a new NRVec. The default constructor just invokes the default
constructor for a MyVec, and then points the reference accordingly. Similarly, the remaining
constructors simply invoke the corresponding MyVec constructors:
Note we are assuming that the syntax of the MyVec class member functions is the same
as that of the NRVec class, but its easy to take care of different syntax. For example, some
vector classes expect arguments in the opposite order: myvec(a,n) instead of myvec(n,a).
Next comes the conversion constructor. It makes a special NRVec that points to MyVecs
data, taking care of MyVec actual argments passed as NRVec formal arguments in function
calls:
Since all the other functions in NRVec access the object only through myref, there is no
need to initialize myvec.
The copy constructor and assignment operator simply call the MyVec copy constructor
and assignment operator:
(We assume here that the MyVec functions do the sensible thing of making a deep copy,
that is, they copy the data, not just the reference. See the MTL example in Appendix B
for a case where this is not true.)
Similarly, functions like size() are implemented by calling the corresponding MyVec
functions:
We pointed out earlier that for the subscript operator, only the form that guarantees
constness of the container is allowed. We want automatic type conversion, and we get that
only for objects passed by const reference. Accordingly, we cannot guarantee constness of
the data (see the discussion in the previous subsection):
Next comes the conversion operator from NRVec to MyVec, which handles NRVec
function return types when used in MyVec expressions:
Finally, the destructor is trivial: the MyVec destructor takes care of destroying the data.
~NRVec() {}
A wrapper class for NRMat follows exactly the same form as for NRVec. The only thing
to watch out for is to make sure that the return type for operator[] is whatever a MyMat
object returns for a single [] dereference. Then expressions like a[i][j] will work correctly.
Instead of listing the final form of the wrapper class here (pulling together all the lines
above), we list in Appendix B two sample nrutil.h files for two popular matrix/vector
libraries. They are for the Template Numerical Toolkit, or TNT [1], and the Matrix Template
Library, or MTL [2]. We have found TNT to be particularly easy to use with the Recipe
functions. All you need to do is use TNT::Vector for MyVec and TNT::Matrix for MyMat. If
you are concerned about poor efficiency in using wrapper classes, our timing experiments show
less than 10% overhead for TNT with most compilers, compared with using TNT directly.
One final instruction: the file nrtypes.h also needs to be changed to the const
protects the container, not the contents convention for passing arguments by reference. To
make this easy, we have supplied the file nrtypes lib.h that correctly defines the constness
of vector and matrix arguments in this way. So simply edit nrtypes.h to include this file
instead of the default nrtypes nr.h (see Appendix B).
A note for C++ aficionados: You can also implement the interface to other matrix/vector
libraries by making NRVec a derived class of your vector class. However, this is not nearly
as elegant as the wrapper class. In particular, it depends on the implementations inside of
your vector class, while the wrapper class uses only the public interface and semantics of
your vector class.