Handling Floating-point Exceptions

If a floating-point exception is disabled (its bit is set to 1 with SETCONTROLFPQQ (x87 arithmetic only), it will not generate an interrupt signal if it occurs. The floating-point process may return an appropriate special value (for example, NaN or signed infinity) or may return an acceptable value (for example, in the case of a denormal operand), and the program will continue. If a floating-point exception is enabled (its bit is set to 0), it will generate an interrupt signal (software interrupt) if it occurs.

The following table lists the floating-point exception signals:

Parameter Name

Value in Hex

Description

FPE$INVALID

#81

Invalid result

FPE$DENORMAL

#82

Denormal operand

FPE$ZERODIVIDE

#83

Divide by zero

FPE$OVERFLOW

#84

Overflow

FPE$UNDERFLOW

#85

Underflow

FPE$INEXACT

#86

Inexact precision

If a floating-point exception interrupt occurs and you do not have an exception handling routine, the run-time system will respond to the interrupt according to the behavior selected by the compiler option /fpe. Remember, interrupts only occur if an exception is enabled (set to 0).

If you do not want the default system exception handling, you need to write your own interrupt handling routine:

Note that your interrupt handling routine must use the cDEC$ ATTRIBUTES option C.

The drawback of writing your own routine is that your exception-handling routine cannot return to the process that caused the exception. This is because when your exception-handling routine is called, the floating-point processor is in an error condition, and if your routine returns, the processor is in the same state, which will cause a system termination. Your exception-handling routine can therefore either branch to another separate program unit or exit (after saving your program state and printing an appropriate message). You cannot return to a different statement in the program unit that caused the exception-handling routine, because a global GOTO does not exist, and you cannot reset the status word in the floating-point processor.

If you need to know when exceptions occur and also must continue if they do, you must disable exceptions so they do not cause an interrupt, then poll the floating-point status word at intervals with GETSTATUSFPQQ (IA-32 architecture only) to see if any exceptions occurred. To clear the status word flags, call the CLEARSTATUSFPQQ (IA-32 architecture only) routine.

Polling the floating-point status word at intervals creates processing overhead for your program. In general, you will want to allow the program to terminate if there is an exception. An example of an exception-handling routine follows. The comments at the beginning of the SIGTEST.F90 file describe how to compile this example.

 ! SIGTEST.F90

 !Establish the name of the exception handler as the

 ! function to be invoked if an exception happens.

 ! The exception handler hand_fpe is attached below.

   USE IFPORT

      INTERFACE

        FUNCTION hand_fpe (sigid, except)

           !DEC$ ATTRIBUTES C :: hand_fpe

           INTEGER(4) hand_fpe

           INTEGER(2) sigid, except

        END FUNCTION

      END INTERFACE

 INTEGER(4) iret

 REAL(4) r1, r2

 r1 = 0.0

 iret = SIGNALQQ(SIG$FPE, hand_fpe)

 WRITE(*,*) 'Set exception handler. Return = ', iret

 ! Cause divide-by-zero exception

 r1 = 0.0

 r2 = 3/r1

 END

 ! Exception handler routine hand_fpe

   FUNCTION hand_fpe (signum, excnum)

     !DEC$ ATTRIBUTES C :: hand_fpe

     USE IFPORT

     INTEGER(2) signum, excnum

     WRITE(*,*) 'In signal handler for SIG$FPE'

     WRITE(*,*) 'signum = ', signum

     WRITE(*,*) 'exception = ', excnum

     SELECT CASE(excnum)

       CASE( FPE$INVALID )

         STOP ' Floating point exception: Invalid number'

       CASE( FPE$DENORMAL )

         STOP ' Floating point exception: Denormalized number'

       CASE( FPE$ZERODIVIDE )

         STOP ' Floating point exception: Zero divide'

       CASE( FPE$OVERFLOW )

         STOP ' Floating point exception: Overflow'

       CASE( FPE$UNDERFLOW )

         STOP ' Floating point exception: Underflow'

       CASE( FPE$INEXACT )

         STOP ' Floating point exception: Inexact precision'

       CASE DEFAULT

         STOP ' Floating point exception: Non-IEEE type'

     END SELECT

     hand_fpe = 1

   END