2-2 CONSTANTS (revised Feb 1998)
*********************************
(Thanks to Craig Burley for the excellent comments, and to Tim prince
for part of the terminology)
GLOSSARY
--------
NAMED CONSTANT A constant which was assigned a name by
a PARAMETER statement
TYPELESS CONSTANT Have no data type at all
UNTYPED CONSTANT Have no well-defined data type
Classification of constants
---------------------------
There are two kinds of FORTRAN constants:
1) Named constants, are declared with the PARAMETER
statement and are used by the name it assigned.
Named constants can be assigned a data type with
the ordinary data type declarations, but are not
variables, in the sense that memory storage is
allocated differently (?).
The data type of the constant is determined either
by an explicit declaration, or by an implicit one
(not recommended), not by the constant's form.
Some compilers support an old non-standard form of
the PARAMETER statement (without parentheses) that
determine the data type by the form, this should
be avoided.
2) Unnamed constants, are used explicitly in the
source code. The compiler infers the data type
from the constant's form, e.g. "1.0E-02".
Some compilers use context-dependent considerations
in inferring the data type, e.g. if the constant
is assigned to a variable, it gets the data type
of this variable.
The Fortran 77 standard allowed using context,
but since this makes the compiler's behaviour
less clear and intuitive, it was disallowed
in Fortran 90.
Another useful classification is to typed and typeless constants.
1) Ordinary constants are typed, i.e. have a well-defined
data type.
2) Typeless constants are specified directly in terms
of the bit patterns representing them in the hardware
and are therefore not portable.
There are two kinds of typeless constants: bit and
Hollerith.
Of course using typeless constants defies the very
idea of portability, one of the main reasons for
using a High-Level programming language, but life
is stronger than rules.
The two classifications are independent of each other, and all four
combinations are legal.
Good programming practice requires using named constants and typed
constants as much as possible (within reasonable limits).
Yet another classification is to numeric and character constants.
The typeless constants are numeric.
Ambiguity in representing "mathematical" constants
--------------------------------------------------
A "mathematical" constant may be represented internally in several
different ways according to the formal data type it belongs to.
For example the mathematical constant "1" could be typed as one of
the different types of integers: INTEGER*1, INTEGER*2, INTEGER etc,
or as one of the floating-point types: REAL, DOUBLE PRECISION, REAL*16.
This "potential polytypism" becomes very important when a constant is
passed to a procedure, the compiler must use then the correct data types.
An INCORRECT example program:
program consts
call sub(1.0)
end
subroutine sub(x)
double precision x
write (*,*) ' x= ', x
return
end
The output of this program, would be probably not the value you
expect (1.0).
Untyped integer and floating-point constants
--------------------------------------------
It was noted above that explicit numeric constants may have several
"potential" data types, this point is best examplified with integers.
In every programming language, if you write an explicit integer constant,
e.g. "100", the compiler may have no way to decide which of the available
integer types is required (some FORTRAN compilers offer INTEGER*1,
INTEGER*2, INTEGER*4 and INTEGER*8 types).
If an untyped integer constant is contained in the range of more
than one of the available integer types, the compiler can't make an
unambiguous decision. It may choose the smallest of the available
types, or a default one (provided it is large enough).
Automatic type promotion (done by the compiler when computing an
arithmetical expression) doesn't solve the problem, on the contrary
it may make it more severe. Conversion to a too 'small' type may
truncate the number, conversion to a too 'large' type may also create
problems, for example the 'too large' type may be:
1) Passed to a procedure that expects a different
integer type.
2) Processed in a wrong way by bit-manipulation
operations, or other direct manipulations of
the internal representations.
In such cases you may get wrong results, and have little or no warning
messages to give you a clue.
Floating-point constants have the same problems (see below), and should
be explicitly typed, if possible.
+-----------------------------------------------------+
| Use PARAMETER statements, and type each constant. |
+-----------------------------------------------------+
Precision of floating-point constants
-------------------------------------
Standard FORTRAN uses only 'typed constants' - every constant has a
data type.
FP constant type Produced if Example
================ ============================= =============
Single-precision No exponent is specified, 1.1
or is specified with an 'E' 0.11E+01
Double-precision Exponent specified with a 'D' 0.11D+001
Quad-precision Exponent specified with a 'Q' 0.11Q+0001
Two remarks:
1) Constants without an exponent part are single-precision
2) Quad-precision (REAL*16 on most machines) is non-standard
Constants may be converted to internal representation at an earlier
stage of the compilation, and then be used with the same type promotion
rules as variables, or a more "intelligent" strategy may be employed
(see discussion above).
In the following code fragment the constant may be converted into a
REAL internal representation, and then converted to a DOUBLE PRECISION
internal representation, losing along all digits that make it different
from the constant "1.0E00":
DOUBLE PRECISION X
.......................
X = 1.0000000999
We see that assigning a single precision constant (a common error),
to a double (or higher) precision variable may cause serious loss of
precision.
Some compilers automatically interpret a single precision constant
as a double precision one if it is assigned to a double variable,
other compilers are said to count the digits and decide accordingly,
Some people consider such 'correcting compilers' dangerous and
misleading by offering these re-interpreting features, even though
some users think they like them.
You should not rely on such features, and anyway they are disallowed
in Fortran 90.
You can check your compiler with the following program
(Precision comparable to 32/64 bits is assumed):
PROGRAM CONSTS
REAL X4, Y4
DOUBLE PRECISION A8, B8, C8
PARAMETER (X4 = 0.4750001E+02,
& Y4 = 0.475E+02)
WRITE(*,*)
WRITE(*,*) ' DOUBLE PRECISION variable = REAL parameter '
WRITE(*,*) ' ========================================== '
A8 = X4
B8 = Y4
C8 = X4 - Y4
WRITE(*,*) ' A8 = ', A8, ' B8 = ', B8, ' C8 = ', C8
WRITE(*,*)
WRITE(*,*) ' DOUBLE PRECISION variable = REAL constant '
WRITE(*,*) ' ========================================= '
A8 = 0.4750001E+02
B8 = 0.475E+02
C8 = A8 - B8
WRITE(*,*) ' A8 = ', A8, ' B8 = ', B8, ' C8 = ', C8
WRITE(*,*)
WRITE(*,*) ' DOUBLE PRECISION variable = default constant '
WRITE(*,*) ' ============================================ '
A8 = 47.50001
B8 = 47.5
C8 = A8 - B8
WRITE(*,*) ' A8 = ', A8, ' B8 = ', B8, ' C8 = ', C8
WRITE(*,*)
END
+---------------------------------------------------------------+
| USE CONSTANTS OF THE SAME TYPE AS THE VARIABLES/EXPRESSIONS |
+---------------------------------------------------------------+
The problem with compile-time arithmetic
----------------------------------------
In almost every program there are computations where the involved
numeric (or character) arguments are already known at compile-time,
compilers attempt to to perform such computations themselves.
The problem with compile-time arithmetic is that parsing expressions,
generating code, and executing it while the compiler runs, requires
having an interpreter built inside the compiler.
There are workarounds: the compiler can write a simple Fortran program
that does the required calculation, compile and run it, via dynamic
or static linking, and read the results either directly or via a file.
Another, simpler way is to treat constants as variables, but store
them in a read-only section of the virtual address space. Of course,
this cannot be done on pseudo operating systems like Windows.
Detecting overflow (underflow) and calling intrinsic functions at
compile-time are difficult for some compilers.
Compile-time overflow (or underflow) of constants
-------------------------------------------------
What happens when at compile-time integer or floating-point constants
are too large or too small? The detection of such problems may be
difficult at compile-time.
Some compilers doesn't handle compile-time arithmetic very well.
The following example program cannot be run in principle on a computer
with 32-bit default integers:
program test
integer i
real a(5)
do i = 1, 5
a(i) = -(10**11) * real(i)
end do
write (*,*) a
write (*,'(5E15.7)') a
end
The problem is with the INTEGER constant "10**11", which is too large,
of course using the REAL constant "10.0**11" eliminates the problem.
On a 64-bit machine this INTEGER constant is inside the representable
range and is legal, on a 32-bit machine it is not. A good compiler
will say there is an arithmetic error while evaluating the constant.
Running a program carrying such a monster may cause it to abort and
display a message that it had an integer overflow.
The very popular GNU g77 and LF77 compilers doesn't give warnings,
and silently produce an erroneous result on a PC. The Fortran 77
compilers of Sun and SGI behave in the same way.
The static program checker FTNCHEK doesn't warn in this case either.
Using named constants modularly
-------------------------------
Named constants are declared in PARAMETER statements, and can have
values that are expressions involving previously defined constants.
Using intrinsic functions in such expressions is not standard
FORTRAN 77, but is supported by some compilers (and should be
supported by g77-0.6).
Using intrinsic functions in PARAMETER statements is not like
using them in the executable part of a program, the PARAMETER
expressions are computed by the compiler, not by the program
at run-time. Some compilers rejects these expressions, others
may die due to segmentation fault or such.
A small example:
integer
* record_size,
* num_records,
* file_size
c ------------------------------------------------------------------
parameter(
* record_size = 80,
* num_records = 1000,
* file_size = record_size * num_records)
c ------------------------------------------------------------------
The bit typeless constants
--------------------------
Bit constants are a kind of typeless constants, and are not standard,
consequently they are implemented differently by different compilers.
The following table may be useful when porting code between different
compilers. Note that such porting is a syntactic matter only if the
architecture of the computer is the same, e.g. porting between two
PC compilers.
When porting between different machines, you should study carefully
how the bit constants are used, and their relation to the hardware.
Probably they were used in an architecture-dependent context,
otherwise typed constants would have been used.
The elipsis '...' is used here to denote a sequence of digits in
the proper range:
Binary [01]
Octal [0-7]
Decimal [0-9]
Hexadecimal [0-9,ABCDEF] or [0-9,abcdef]
| Binary | Octal | Decimal | Hexadecimal |
-----------|----------|----------|----------|-------------|
VMS | '...'B | '...'O | | '...'X |
| | | | '...'Z |
-----------|----------|----------|----------|-------------|
g77 | '...'B | '...'O | | '...'X |
| B'...' | O'...' | | X'...' |
| | | | '...'Z |
| | | | Z'...' |
-----------|----------|----------|----------|-------------|
Microsoft | | | | 16#... |
| | | | |
-----------|----------|----------|----------|-------------|
| | | | |
| | | | |
-----------|----------|----------|----------|-------------|
Hollerith constants
--------------------
The other kind of typeless constant is a relic from Fortran versions
predating the Fortran 77 standard, in those days Fortran didn't have
a CHARACTER data type, and character data was stored in INTEGER
variables.
The Hollerith constants were useful in the conversion, they shifted
the work of computing the equivalet integers to the compiler.
INTEGER ISTR
..............
ISTR = 4HABCD
The assignment above is equivalent (for 4-byte integers, and 1-byte
characters) to:
ISTR = (256**3 * ICHAR('A')) +
& (256**2 * ICHAR('B')) +
& (256 * ICHAR('C')) +
& ICHAR('D')
An example program:
program holler
integer istr, i1, i2, i3, i4
istr = 4HABCD
write (*,*) 'The equivalent integer: ', istr
write (*,'(1X,A,A4)') 'Writing with A format: ', istr
i1 = mod(istr, 256)
istr = (istr - i1) / 256
i2 = mod(istr, 256)
istr = (istr - i2) / 256
i3 = mod(istr, 256)
istr = (istr - i3) / 256
i4 = istr
write (*,*) 'Extracting byte by byte: ',
& char(i1), char(i2), char(i3), char(i4)
end
The introduction of the CHARACTER data type made these constants
superfluous, and introduced a new problem. The relative size of a
character and the older numeric types couldn't be specified in a
standard way, it varied upon existing implementations.
To keep using storage equivalencing tricks in a portable way,
Fortran 77 disallowed using numeric and character data in the same
common block.
Readability enhancement in Fortran 77
-------------------------------------
You can use SPACE characters inside constants to enhance readability,
for example:
PARAMETER(NMAX = 1 200 000)
You can't use spaces inside constants in F90 free form, which is
likely to be the _only_ form available someday.
Return to contents page