Underflows
Here is a summary of how FTN95 handles underflow. Details appear below. By
default FTN95 sets underflow as unmasked so that when a underflow occurs an
exception is generated. FTN95 handles this exception internally and by default
allows the computation to continue. A call to the routine MASK_UNDERFLOW@ sets
underflow as masked so that an underflow does not generate an exception. A call
of PERMIT_UNDERFLOW@(.FALSE.) resets underflow as unmasked and also triggers a
run time error when FTN95 handles the exception internally.
Intel architectures use IEEE format floating point numbers. An underflow occurs
when the result of a floating point operation is too small to be represented by
the floating point format. The discussion below is illustrated using extended
precision as an example. Single and double precision underflows are similar.
If x=1E3000 then y=x*x cannot be represented because the result is 1E6000
which is smaller than the smallest normalised floating point number
(approximately 1E4932). y would be set to zero.
The floating point unit has a masked and an unmasked response to underflow. In
the above example y is set to zero in both cases. However, the mechanisms are
quite different. With the masked response, the actual result is zero and is
directly assigned. The unmasked response is to generate an underflow exception
which is trapped by the run time library. The failing instruction is decoded
and the target register or storage is replaced with zero. Unless
PERMIT_UNDERFLOW@(.FALSE.) is used, control is then returned to the program at
the following instruction and execution continues as normal.
Denormals
Denormals arise for one of two reasons: a) when an integer or an unset variable
is passed as an argument that should be an floating point number and b) when
the result of a floating point calculation is too small to be a normalised
number but still large enough to have a representation (this only occurs when
using the masked response to underflow).
In case (b), when the smallest exponent of the number is reached, leading zeroes
are introduced into the significand of the number. Thus precision is lost for
these numbers. Smaller numbers introduce more leading zeroes and this continues
until no more leading zeros can be introduced and the result becomes zero. This
process is called gradual underflow. Only when the number is less than about
1E4951 does it become zero.
For example, the hex pattern below is the smallest positive extended precision
normalised number. This is defined as such in the IEEE specification.
Exponent

Significand

0001

8000000000000000 (64 bits of precision)

Note that the most significant bit of the significand is 1 (8 is 1000 in
binary). This is the normalisation bit. Every valid floating point number is
shifted so that this bit is 1. This allows 64 bits of precision because there
are no leading zeroes. However, if a smaller number is generated, the exponent
is 0 and a leading zero is introduced into the significand:
Exponent

Significand

0000

4000000000000000 (63 bits of precision)

The number is then termed a denormal. The smallest denormal is:
Exponent

Significand

0000

0000000000000001 (1 bit of precision)

Although precision is lost for these extremely small values, the unmasked
response of replacing them with zero represents a total loss of precision.
Denormals, therefore give better numerical results although at times they may
be nonzero when zero was expected. Denormals are handled like any other number
and are printed correctly by the I/O system.
If underflows become unmasked, an attempt to use a denormal results in a run
time error whilst the output field for a denormal is filled with "?".
Masking underflows
Underflows can be masked by calling the subroutine MASK_UNDERFLOW@
SUBROUTINE MASK_UNDERFLOW@()
Underflows can be unmasked by calling the subroutine UNMASK_UNDERFLOW@
SUBROUTINE UNMASK_UNDERFLOW@()
This is the default. If one of these is used, it should be the first executable
line in your program. You are very strongly advised not to change the underflow
state after this. Calling PERMIT_UNDERFLOW@(.FALSE.) causes underflows to
become unmasked. The environment variable SALFENVAR can be set to
MASK_UNDERFLOW or UNMASK_UNDERFLOW in order to provide a default state for your
programs. If there is no environment variable setting, then the default is to
unmask underflows.
Summary
Masking underflows provides a very safe way of handling
underflows and achieving better numerical results, although you should be aware
of the issues involved.
The table below shows the range of positive floating point values.

Largest

Smallest

Smallest denormal(masked underflows)

Single precision

1E38

1E38

1E46

Double precision

1E308

1E308

1E324

Extended precision

1E4932

1E4932

1E4951
