Fortran: Range and Precision
The range and precision of numeric types is as important in Fortran as it is in any other programming language. Perhaps even more so given Fortran's strengths in the field of numerical analysis. Historically (ie. prior to Fortran 90) the only portable method for improving the precision of real and complex datatypes was the use of DOUBLE PRECISION. As its name suggests, this type only provided double whatever precision went before and gave no clue to the programmer or user whether this extended precision was actually adequate for the program's requirements.
With the release of Fortran 90 came the introduction of kind types and the associated functions which allow the conscientious programmer to specify in the code the parameters of range and precision required.
Integer range
When dealing with integer values we are concerned only with the range of possible values. Typically (but neither exclusively nor portably) a 32-bit architecture would have an integer range of -231 to 231 - 1 and a 64-bit architecture -263 to 263 - 1. These will be fine for many tasks but we might wish to alter the range.
To achieve this we use the intrinsic function SELECTED_INT_KIND() to generate a kind value for the required range. This is an integer function which takes an integer as its sole argument that is the required power of 10 of the range to be supported. The resultant kind value, stored in a parameter, can then be used to declare variables of the correct range.
For example, let us suppose we wished to use integers as high as 1012. We could employ this function in this manner:
INTEGER, PARAMETER :: large = SELECTED_INT_KIND (12)
INTEGER (large) :: alpha, beta
Then the variables alpha and beta will have the capacity to store the very large (or very small) integers which we require.
Real range and precision
For reals there is the precision to consider in addition to the range and in many tasks it is the precision which is more important. However, the method of altering the precision is analogous and we can use the equivalent SELECTED_REAL_KIND() function to produce the kind number which we need. This function takes two arguments, the second of which is the range as a power of 10 just as it is in the case of integers with the caveat that it now also applies to negative powers of 10 too. The first argument is the precision and is the number of significant figures which the type will support.
INTEGER, PARAMETER :: precise = SELECTED_REAL_KIND (16,100)
REAL (precise) :: zeta, theta
In this case the variables zeta and theta will accept any real value with orders of magnitude up to +/-100 and precise to 16 significant figures.
Complex
Each component of a complex number has a real coefficient and therefore each of these real coefficients has a real range and precision just like a real variable. As such we use the same kind types for complex as we do for real. eg.
INTEGER, PARAMETER :: precise = SELECTED_REAL_KIND (16,100)
COMPLEX (precise) :: omega
This gives both the real part and the real coefficient of the imaginary part of the complex variable omega the specified range (10+/-100) and precision (16 significant figures).
Literals
When using extended precisions or ranges it is extremely important to remember to convert literal numbers within the code to these new kinds, otherwise precision can be lost. This is achieve by appending the literal value with an underscore and then the kind parameter. eg.
INTEGER, PARAMETER :: precise = SELECTED_REAL_KIND (16,100)
REAL (precise) :: twopi
twopi = 3.14159265358979323846_precise * 2.0_precise
Without these adornments the expression on the right-hand side of the assignment operator would be computed in terms of standard REAL values and precision would be irrevocably lost before it is stored in the extended-precision variable. This is a very easy mistake to make and a difficult one to spot.
Further reading
- Goldberg, D., What Every Computer Scientist Should Know About Floating-Point Arithmetic, Computing Surveys, 1991