Fork me on GitHub

Managing kind parameters for numeric types in Fortran 2003

Before the discussion, here is a reminder of relevant Fortran 2003 points:

  • For a given numeric data type, a Fortran compiler may offer differents kinds of this type.
  • Different kinds of integer mean different ranges of integers. Different kinds of real or complex mean different numbers of significant digits and different ranges for the exponent.
  • For the real and complex types, the Fortran standard requires at least to kinds: default and double precision.
  • When you declare a variable without specifying a kind, that is with just the type integer, real or complex, the default kind for this type is selected.

Should you always specify a kind? For a given type, the compiler may offer a "smaller" kind than the default kind: with a smaller range or less precision. So you may be tempted to always choose the "smallest" kind compatible with your program. It is true that variables of a smaller kind occupy less memory. So if you have large arrays, it may be worth declaring them with a kind smaller than the default kind (if you do not need the precision or the range of the default kind). From the point of view of computation speed, however, the advantage is less clear. The default kind corresponds to the "word length" of your processor and the processor is optimized to process values with such length. So you may not gain anything in speed by choosing a kind smaller than the default kind. General advice: unless you want to save memory occupied by large arrays, do not bother to choose a kind smaller than the default kind.

Let us now consider the case of a program unit which fundamentally needs a high range or precision for some of its variables: for exemple for the algorithm to converge or for the results to be meaningful. In other words, the need may be expressed by numerical analysis as a required number of decimal digits, or a range of integer values or a range of exponents for real values. Fortran offers the intrinsic functions selected_int_kind and selected_real_kind, which allow to choose a kind according to these requirements from numerical analysis. So this is the way to go: define a named constant (that is, with the parameter attribute) for the appropriate kind, using selected_int_kind or selected_real_kind, then declare objects using this named constant.

Another case is when a procedure does not require by itself some minimum precision or range, but you want to allow a user to call this procedure with all possible kinds for some of the actual arguments (depending on the needs of the calling program). This is a typical case for a procedure in a library, so let us assume your procedure is in a library. There are, in general, three ways to do this in Fortran:

  • Write the procedure using the default kind (that is, do not specify any kind at declaration) and rely on a compiler option that changes the precision of the default kind. You have to compile the library and the calling program with this option.
  • Duplicate the procedure, and make the appropriate changes for each kind. It is a good idea in this case to provide a generic interface for the resulting set of procedures.
  • Define a named constant (parameter) for the kind. Declare objects in the procedure using this named constant. You have to recompile the library when you change the value of the named constant.

What are the pros and the cons of these three ways?

The advantage of the first way, relying on a compiler option, is that, obviously, it is the easy way out for the programmer of the library. However, this way has some drawbacks. First, it lacks the quality of "portability". In other words, you cannot be sure this way will work on any system that has a Fortran compiler: maybe the required compiler option does not exist for the compiler chosen by the person who will use your library. Second, it lacks subtlety: all the variables in your file are affected, and maybe your procedures do not want this general change. Furthermore, for some compiler, the only available option may change the default kind of several types together (for example, real and integer), which may not be desirable. On the whole, this way should be avoided.

The second way, duplicating the procedure, avoids the portability problem and allows control on the kind of each variable, separately. Another advantage of this way is that it is the most comfortable way for the user of the libray: the procedures for the various kinds are just available without any user action. On the other hand, this is the most tedious way for you, the programmer of the libray: it may amount to a large number of lines of code; you have to keep duplication perfect as you maintain the duplicated procedures. General advice: go this way for a "small" procedure.

The third way is the middle way: it requires less work than the second way for the programmer of the library, but it is less comfortable for the user of the libray. General advice: go this way for a "large" procedure.

links

social