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:

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:

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:

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:

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:

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