# Compaq Fortran
User Manual for
Tru64 UNIX and
Linux Alpha Systems

Previous | Contents | Index

# Chapter 14
Controlling Floating-Point Exceptions

Note This chapter applies only to Compaq Fortran Tru64 UNIX systems.

This chapter contains the following topics:

- Section 14.1, Overview of Controlling Floating-Point Exceptions
- Section 14.2, Using the for_fpe_flags.f File
- Section 14.3, Calling the for_get_fpe and for_set_fpe Functions
- Section 14.4, File fordef.f and Its Usage

# 14.1 Overview of Controlling Floating-Point Exceptions

This chapter contains information about controlling exceptions that can occur during the run-time processing of floating-point numbers. These exceptions are underflow, division by zero, overflow, and invalid operation (such as the square root of a negative number).

See Section 9.4, Native IEEE Floating-Point Representations and Exceptional Values for information about the internal representation of floating-point numbers including exceptional values (such as plus infinity).

You can use these command-line options to direct the processing of Compaq Fortran floating-point numbers at run time:

- `-fpen` option to control exception handling and reporting (see Section 3.44)
- `-check underflow` option to control checking for floating-point underflow at run time (see Section 3.29)
- `-synchronous_exceptions` option to control reporting of exceptions at run time (see Section 3.86)
- `-fprm keyword` option to control rounding of floating-point operations (see Section 3.46)

# 14.2 Using the for_fpe_flags.f File

The `-fpen` option, as explained in Section 3.44, lets you control floating-point exceptions according to the value of `n` . Table 3-3 contains the values of `n` and the corresponding results at run time when floating-point exceptions occur. These exceptions are underflow, division by zero, overflow, and invalid operation (such as the square root of a negative number).

Normally the `-fpen` option, along with the `-check underflow` and `-synchronous_exceptions` options, gives you adequate control over floating-point exception handling and reporting.

However, if any combination of these three options does not give you adequate control, then you can use the following:

- File `/usr/include/for_fpe_flags.f` , which contains definitions of the bits in an INTEGER*4 variable. (See Section 14.2.1, Bit Definitions in File for_fpe_flags.f.) This file is a file of Fortran statements with flags that relate to floating-point exceptions.
- Functions `for_get_fpe` and `for_set_fpe` . (See Section 14.3, Calling the for_get_fpe and for_set_fpe Functions.)
- Option `-fpe3` .

For example, suppose you want to read a denormalized number from an unformatted file and perform calculations on it without generating an exception about an invalid operation. Suppose you also want to have divide-by-zero exceptions trapped and reported. While the `-fpe3` option meets the first requirement, it also results in no trapping and reporting of divide-by-zero exceptions.

The general steps of the solution to this example are:

1. Include, in your source program, file `/usr/include/for_fpe_flags.f` .
2. Include statements that select, from the included file, bits corresponding to the phrases "trap divide-by-zero exceptions" and "report divide-by-zero exceptions." Include them in the INTEGER*4 argument to function `for_set_fpe` .
3. Compile the program with the `-fpe3` option. As long as the reference to function `for_set_fpe` occurs at the beginning of the program, division-by-zero exceptions will be trapped and reported. (When function `for_set_fpe` executes, it overrides the initial effect of the `-fpe3` option: to disable trapping and reporting of divide-by-zero exceptions.)

This example reappears later, in program `fpe_div0_msg.f90` in Section 14.3.2.

Note Normally the `-fpen` , `-check underflow` , `-synchronous_exceptions` , and `-fprm keyword` options give you sufficient control over the processing of floating-point exceptions. Using file `/usr/include/for_fpe_flags.f` and function `for_set_fpe` can yield unanticipated results.

## 14.2.1 Bit Definitions in File for_fpe_flags.f

Table 14-1 explains the bit definitions in file `/usr/include/for_fpe_flags.f` . The compiler automatically generates a call to `for_set_fpe()` , which is expected when the program begins execution. The argument to `for_set_fpe()` has the appropriate bits set (as defined in `for_fpe_flags.f` ) to achieve the documented behavior for the `-fpen` option specified at compile time. You have the option of changing this bit setting according to the individual bit definitions and their corresponding effects in Table 14-1. The function `for_set_fpe` works with the bit definitions to make the change.

Note For the default `-fpe0` option, the compiler does not generate the call to `for_set_fpe()` . The run-time library automatically initializes floating-point exception behavior in this case at run-time initialization.

Table 14-1 Bit Definitions in File for_fpe_flags.f
Bit Name | Effect When Set
--- | ---
FPE_M_TRAP_UND | Requests delivery of underflow traps to the current signal handler.
FPE_M_TRAP_OVF | Requests delivery of overflow traps to the current signal handler.
FPE_M_TRAP_DIV0 | Requests delivery of division-by-zero traps to the current signal handler.
FPE_M_TRAP_INV | Requests delivery of invalid operation traps to the current signal handler.
FPE_M_MSG_UND | If the current signal handler is the default handler provided with the Fortran run-time library and bit FPE_M_TRAP_UND is also set, then the default handler sends a message to `stderr` for each of the first two occurrences of underflow traps. The default handler also sends a total count of underflow traps to `stderr` when the program terminates. If the current signal handler is not the default handler, then bit FPE_M_MSG_UND has no effect.
FPE_M_MSG_OVF | If the current signal handler is the default handler provided with the Fortran run-time library and bit FPE_M_TRAP_OVF is also set, then the default handler sends a message to `stderr` for each of the first two occurrences of overflow traps. The default handler also sends a total count of overflow traps to `stderr` when the program terminates. If the current signal handler is not the default handler, then bit FPE_M_MSG_OVF has no effect.
FPE_M_MSG_DIV0 | If the current signal handler is the default handler provided with the Fortran run-time library and bit FPE_M_TRAP_DIV0 is also set, then the default handler sends a message to `stderr` for each of the first two occurrences of divide-by-zero traps. The default handler also sends a total count of divide-by-zero traps to `stderr` when the program terminates. If the current signal handler is not the default handler, then bit FPE_M_MSG_DIV0 has no effect.
FPE_M_MSG_INV | If the current signal handler is the default handler provided with the Fortran run-time library and bit FPE_M_TRAP_INV is also set, then the default handler sends a message to `stderr` for each of the first two occurrences of invalid operation traps. The default handler also sends a total count of invalid operation traps to `stderr` when the program terminates. If the current signal handler is not the default handler, then bit FPE_M_MSG_INV has no effect.
FPE_M_ABRUPT_UND | Replaces denormalized numbers obtained as results of calculations with zeroes.
FPE_M_ABRUPT_OVF | Reserved.
FPE_M_ABRUPT_DIV0 | Reserved.
FPE_M_ABRUPT_INV | Reserved.
FPE_M_ABRUPT_DMZ | Replaces denormalized numbers used as input operands to floating-point instructions with zeroes. This does not have anything to do with reading data from unformatted files.

# 14.3 Calling the for_get_fpe and for_set_fpe Functions

Table 14-1 contains a list of bit definitions (in an INTEGER*4 variable) and their run-time effects when floating-point exceptions occur. Functions `for_get_fpe` and `for_set_fpe` read and set the bits, respectively, after a program begins execution. (The `-fpen` option also sets these bits when a program begins execution.)

## 14.3.1 Calling for_get_fpe

Function `for_get_fpe` has no argument. It returns an INTEGER*4 variable whose bits specify how the run-time library currently handles floating-point exceptions. The bits of the variable are defined in Table 14-1. `for_get_fpe` must be declared INTEGER(4).

For example, consider the following program. When compiled with the default `-fpe0` option, this program displays the default settings of the bits in the INTEGER*4 variable:

          PROGRAM TESTFGPE_FPE0
    !
    !     This program returns floating-point exception flags
    !        using function for_get_fpe().
    !
          integer*4 fpe_flags
          integer*4 for_get_fpe
          external for_get_fpe
    !
          PRINT *, ''
          PRINT *, 'Start of program'
          fpe_flags = for_get_fpe()
          PRINT *, ''
          PRINT *, 'for_get_fpe() has returned, with option fpe0:'
          WRITE (*, 200) fpe_flags
    200   FORMAT (' ', 'In B32 format: ', B32)
          PRINT *, ''
          PRINT *, 'End of program'

          END PROGRAM TESTFGPE_FPE0

The compilation and execution commands are:

    % f90 -fpe0 testfgpe_fpe0.f90
    % a.out

The output from this program, with spaces added to the 32-character representation of variable fpe_flags, is:

    Start of program

    for_get_fpe() has returned, with option fpe0:
    In B32 format:                1 0000 0000 0000 1110

    End of program

Compiling this program with a different value of the `-fpen` option gives different output. For example, if you compile with `-fpe3` , variable fpe_flags contains zero.

For More Information:

- On the `for_get_fpe` function, see `for_set_fpe(3f)`.

## 14.3.2 Calling for_set_fpe

Function `for_set_fpe` has a single argument: a floating-point exception behavior mask. This is an INTEGER*4 variable whose bits specify how the run-time library will handle floating-point exceptions. Function `for_set_fpe` must be declared INTEGER(4).

The bits of the mask variable are defined in Table 14-1. When the function executes, it both returns the previous value of the mask and changes the mask to the value of the argument. Recall that the `-fpen` option determines the value of the mask when the program begins to execute. Function `for_set_fpe` allows you to change the mask, and the handling of floating-point exceptions, after the program begins to execute.

Recall in Section 14.2 the example where the requirements are to read a denormalized number from an unformatted file and perform calculations on it without generating an invalid operation exception. Also, divide-by-zero exceptions must be trapped and reported. The `-fpe3` option meets the first requirement. File `/usr/include/for_fpe_flags.f` and function `for_set_fpe` work together to meet the second requirement.

Consider program `fpe_div0_msg.f90` :

          program fpe_div0_msg
    !     Trap when division by zero occurs and display a message.
    !     Compile with the command   f90 -fpe3 fpe_div0_msg.f90
          external for_set_fpe      ! External function

          integer*4 for_set_fpe

    !     Include the file with the definitions of the
    !        floating-point exceptions.
          include '/usr/include/for_fpe_flags.f'
          real*4 a,b,c
          integer*4 old_fpe_flags, new_fpe_flags
    !     Set the bits, of the argument to for_set_fpe(), to trap
    !        when division by zero occurs and to display a message.
          new_fpe_flags = FPE_M_TRAP_DIV0 + FPE_M_MSG_DIV0
          old_fpe_flags = for_set_fpe(new_fpe_flags)
          a = 5.0
          print *, ''
          print *, 'Give me 0.0'
          read *, b
          print *, 'Division by zero is next'
          c = a / b
          print *, 'The result of division by zero is', c
          print *, ''
          end

In this program, at run time, division by zero occurs. This floating-point exception results in:

1. A trap and its delivery to the current signal handler
2. Reporting by the default signal handler provided with the Compaq Fortran run-time library
3. Continuation of the program

The compilation and execution commands are:

    % f90 -fpe3 fpe_div0_msg.f90
    % a.out

The output from this program and user input (0.0) are:

     Give me 0.0
    0.0
     Division by zero is next
    forrtl: error (73): floating divide by zero
     The result of division by zero is Infinity

    forrtl: info (299): 1 floating divide-by-zero traps

Setting bit `fpe_m_trap_div0` causes a trap when division by zero occurs and delivery of a signal to the current signal handler. Setting bit `fpe_m_msg_div0` causes the Compaq Fortran run-time routines (the current and default signal handler) to display a message about the trapped division-by-zero floating-point exception. Because of compilation with `-fpe3` , function `for_set_fpe` returns zero to variable old_fpe_flags.

For More Information:

- On the `for_set_fpe` function, see `for_set_fpe(3f)`.

# 14.4 File fordef.f and Its Usage

The parameter file `/usr/include/fordef.f` contains symbols and INTEGER*4 values corresponding to the classes of floating-point representations. Some of these classes are exceptional ones such as bit patterns that represent positive denormalized numbers. See Section 9.4.8, Exceptional Floating-Point Representations.

With this file of symbols and with the FP_CLASS intrinsic function, you have the flexibility of identifying exceptional numbers so that, for example, you can replace positive and negative denormalized numbers with true zero.

The following is a simple example of identifying floating-point bit representations:

    include '/usr/include/fordef.f'
    real*4 a
    integer*4 class_of_bits
    a = 57.0   ! Bit pattern is an Alpha finite number
    class_of_bits = fp_class(a)
    if ( class_of_bits .eq. for_k_fp_pos_norm  .or.   &
         class_of_bits .eq. for_k_fp_neg_norm       ) then
       print *, a, ' is a non-zero and non-exceptional value'
    else
       print *, a, ' is zero or an exceptional value'
    end if
    end

In this example, the symbol `for_k_fp_pos_norm` in file `/usr/include/fordef.f` plus the REAL*4 value 57.0 to the FP_CLASS intrinsic function results in the execution of the first print statement.

Table 14-2 explains the symbols in file `/usr/include/fordef.f` and their corresponding floating-point representations. Section 9.4.8, Exceptional Floating-Point Representations explains each representation.

Table 14-2 Symbols in File fordef.f
Symbol Name | Class of Floating-Point Bit Representation
--- | ---
FOR_K_FP_SNAN | Signaling NaN
FOR_K_FP_QNAN | Quiet NaN
FOR_K_FP_POS_INF | Positive infinity
FOR_K_FP_NEG_INF | Negative infinity
FOR_K_FP_POS_NORM | Positive normalized finite number
FOR_K_FP_NEG_NORM | Negative normalized finite number
FOR_K_FP_POS_DENORM | Positive denormalized number
FOR_K_FP_NEG_DENORM | Negative denormalized number
FOR_K_FP_POS_ZERO | Positive zero
FOR_K_FP_NEG_ZERO | Negative zero

Another example of using file `fordef.f` and intrinsic function FP_CLASS follows. The goals of this program are to quickly read any 32-bit pattern into a REAL*4 number from an unformatted file with no exception reporting and to replace denormalized numbers with true zero:

          include '/usr/include/fordef.f'
          real*4 a(100)
          integer*4 class_of_bits
    !     open an unformatted file as unit 1
    !     ...
          read (1) a
          do i = 1, 100
             class_of_bits = fp_class(a(i))
             if ( class_of_bits .eq. for_k_fp_pos_denorm  .or.   &
                  class_of_bits .eq. for_k_fp_neg_denorm       ) then
                a(i) = 0.0
             end if
          end do
          close (1)
          end

You can compile this program with any value of `-fpen` . Intrinsic function FP_CLASS helps to find and replace denormalized numbers with zeroes before the program can attempt to perform calculations on the denormalized numbers. On the other hand, if this program did not replace denormalized numbers read from unit 1 with zeroes and the program was compiled with `-fpe0` , then the first attempted calculation on a denormalized number would result in a floating-point exception.

File `fordef.f` and intrinsic function FP_CLASS can work together to identify NaNs. A variation of the previous example would contain the symbols `for_k_fp_snan` and `for_k_fp_qnan` in the IF statement. A faster way to do this is based on the intrinsic ISNAN function. One modification of the previous example, using ISNAN, follows:

    !     The ISNAN function does not need file /usr/include/fordef.f
          real*4 a(100)
    !     open an unformatted file as unit 1
    !     ...
          read (1) a
          do i = 1, 100
             if ( isnan (a(i)) ) then
                print *, 'Element ', i, ' contains a NaN'
             end if
          end do
          close (1)
          end

You can compile this program with any value of `-fpen` .

Previous | Next | Contents | Index