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