The following interfaces help people use IEEE arithmetic. These are mostly in the math library libsunmath and in several .h files.
ieee_flags(3m)--Controls rounding direction and rounding precision; query exception status; clear exception status
ieee_handler(3m)--Establishes an exception handler routine
ieee_functions(3m)--Lists name and purpose of each IEEE function
ieee_values(3m)--Lists functions that return special values
Other libm functions described in this section:
ieee_retrospective
nonstandard_arithmetic
standard_arithmetic
The SPARC processors conform to the IEEE standard in a combination of hardware and software support for different aspects. x86 processors conform to the IEEE standard entirely through hardware support.
The newest SPARC processors contain floating-point units with integer multiply and divide instructions and hardware square root.
Best performance is obtained when the compiled code properly matches the runtime floating-point hardware. The compiler's -xtarget= option permits specification of the runtime hardware. For example, -xtarget=ultra would inform the compiler to generate object code that will perform best on an UltraSPARC processor.
On SPARC platforms: The utility fpversion displays which floating-point hardware is installed and indicates the appropriate -xtarget value to specify. This utility runs on all Sun SPARC architectures. See fpversion(1), the Sun Fortran User's Guide (regarding -xtarget) and the Numerical Computation Guide for details.
The ieee_flags function is used to query and clear exception status flags. It is part of the libsunmath library shipped with Sun compilers and performs the following tasks:
Controls rounding direction and rounding precision
Checks the status of the exception flags
Clears exception status flags
The general form of a call to ieee_flags is:
flags = ieee_flags( action, mode, in, out )
Each of the four arguments is a string. The input is action, mode, and in. The output is out and flags. ieee_flags is an integer-valued function. Useful information is returned in flags as a set of 1-bit flags. Refer to the man page for ieee_flags(3m) for complete details.
Possible parameter values are shown in the following table:
Table 6-1 ieee_flags( action, mode, in, out ) Argument Values
action |
mode |
in, out |
---|---|---|
get set clear clearall |
direction precision exception |
nearest tozero negative positive extended double single inexact division underflow overflow invalid all common |
The precision mode is available only on x86 platforms.
Note that these are literal character strings, and the output parameter out must be at least CHARACTER*9. The meanings of the possible values for in and out depend on the action and mode they are used with. These are summarized in the following table:
Table 6-2 ieee_flags Argument Meanings
Value of in and out |
Refers to |
---|---|
nearest, tozero, negative, positive |
Rounding direction |
extended, double, single |
Rounding precision |
inexact, division, underflow, overflow, invalid |
Exceptions |
all |
All five exceptions |
common |
Common exceptions: invalid, division, overflow |
For example, to determine what is the highest priority exception that has a flag raised, pass the input argument in as the null string:
CHARACTER *9, out ieeer = ieee_flags( 'get', 'exception', '', out ) PRINT *, out, ' flag raised'
Also, to determine if the overflow exception flag is raised, set the input argument in to overflow. On return, if out equals overflow, then the overflow exception flag is raised; otherwise it is not raised.
ieeer = ieee_flags( 'get', 'exception', 'overflow', out ) IF ( out.eq. 'overflow') PRINT *,'overflow flag raised'
Example: Clear the invalid exception:
ieeer = ieee_flags( 'clear', 'exception', 'invalid', out )
Example: Clear all exceptions:
ieeer = ieee_flags( 'clear', 'exception', 'all', out )
Example: Set rounding direction to zero:
ieeer = ieee_flags( 'set', 'direction', 'tozero', out )
Example: Set rounding precision to double:
ieeer = ieee_flags( 'set', 'precision', 'double', out )
Calling ieee_flags with an action of clear, as shown in the following example, resets any uncleared exceptions. Put this call before the program exits to suppress system warning messages about floating-point exceptions at program termination.
Example: Clear all accrued exceptions with ieee_flags():
i = ieee_flags('clear', 'exception', 'all', out )
The following example demonstrates how to determine which floating-point exceptions have been raised by earlier computations. Bit masks defined in the system include file f77_floatingpoint.h are applied to the value returned by ieee_flags.
In this example, DetExcFlg.F, the include file is introduced using the #include preprocessor directive, which requires us to name the source file with a .F suffix. Underflow is caused by dividing the smallest double-precision number by 2.
Example: Detect an exception using ieee_flags and decode it:
#include "f77_floatingpoint.h" CHARACTER*16 out DOUBLE PRECISION d_max_subnormal, x INTEGER div, flgs, inv, inx, over, under x = d_max_subnormal() / 2.0 ! Cause underflow flgs=ieee_flags('get','exception','',out) ! Which are raised? inx = and(rshift(flgs, fp_inexact) , 1) ! Decode div = and(rshift(flgs, fp_division) , 1) ! the value under = and(rshift(flgs, fp_underflow), 1) ! returned over = and(rshift(flgs, fp_overflow) , 1) ! by inv = and(rshift(flgs, fp_invalid) , 1) ! ieee_flags PRINT *, "Highest priority exception is: ", out PRINT *, ' invalid divide overflo underflo inexact' PRINT '(5i8)', inv, div, over, under, inx PRINT *, '(1 = exception is raised; 0 = it is not)' i = ieee_flags('clear', 'exception', 'all', out) ! Clear all END
Example: Compile and run the preceding example (DetExcFlg.F):
demo% f77 -silent DetExcFlg.F demo% a.out Highest priority exception is: underflow invalid divide overflo underflo inexact 0 0 0 1 1 (1 = exception is raised; 0 = it is not) demo%
The compilers provide a set of functions that can be called to return a special IEEE extreme value. These values, such as infinity or minimum normal, can be used directly in an application program.
Example: A convergence test based on the smallest number supported by the hardware would look like:
IF ( delta .LE. r_min_normal() ) RETURN
The values available are listed in the following table:
Table 6-3 Functions Returning IEEE Values
IEEE Value |
Double Precision |
Single Precision |
---|---|---|
infinity |
d_infinity() |
r_infinity() |
quiet NaN |
d_quiet_nan() |
r_quiet_nan() |
signaling NaN |
d_signaling_nan() |
r_signaling_nan() |
min normal |
d_min_normal() |
r_min_normal() |
min subnormal |
d_min_subnormal() |
r_min_subnormal() |
max subnormal |
d_max_subnormal() |
r_max_subnormal() |
max normal |
d_max_normal() |
r_max_normal() |
The two NaN values (quiet and signaling) are unordered and should not be used in comparisons such as IF(X.ne.r_quiet_nan())THEN... To determine whether some value is a NaN, use the function ir_isnan(r) or id_isnan(d).
The Fortran names for these functions are listed in these man pages:
libm_double(3f)
libm_single(3f)
ieee_functions(3m)
Also see:
ieee_values(3m)
The f77_floatingpoint.h header file
Typical concerns about IEEE exceptions are:
What happens when an exception occurs?
How do I use ieee_handler() to establish a user function as an exception handler?
How do I write a function that can be used as an exception handler?
How do I locate the exception--where did it occur?
Exception trapping to a user routine begins with the system generating a signal on a floating-point exception. The standard UNIX name for signal: floating-point exception is SIGFPE. The default situation on SPARC and x86 platforms is not to generate a SIGFPE when an exception occurs. For the system to generate a SIGFPE, exception trapping must first be enabled, usually by a call to ieee_handler().
To establish a function as an exception handler, pass the name of the function to ieee_handler(), together with the name of the exception to watch for and the action to take. Once you establish a handler, a SIGFPE signal is generated whenever the particular floating-point exception occurs, and the specified function is called.
The form for invoking ieee_handler() is shown in the following table:
Table 6-4 Arguments for ieee_handler(action, exception, handler)
Argument |
Type |
Possible Values |
action |
character |
get, set, or clear |
exception |
character |
invalid, division, overflow, underflow, or inexact |
handler |
Function name |
The name of the user handler function or SIGFPE_DEFAULT, SIGFPE_IGNORE, or SIGFPE_ABORT |
Return value |
integer |
0 =OK |
The routine that calls ieee_handler() should also declare:
#include 'f77_floatingpoint.h'
For f90 programs, declare:
#include 'f90/floatingpoint.h'
The special arguments SIGFPE_DEFAULT, SIGFPE_IGNORE,and SIGFPE_ABORT are defined in f77_floatingpoint.h and can be used to change the behavior of the program for a specific exception:
SIGFPE_DEFAULT or SIGFPE_IGNORE |
No action taken when the specified exception occurs.
|
SIGFPE_ABORT |
Program aborts, possibly with dump file, on exception. |
The actions your exception handler takes are up to you. However, the routine must be an integer function with three arguments specified as shown:
handler_name( sig, sip, uap )
handler_name is the name of the integer function.
sig is an integer.
sip is a record that has the structure siginfo.
uap is not used.
Example: An exception handler function:
INTEGER FUNCTION hand( sig, sip, uap ) INTEGER sig, location STRUCTURE /fault/ INTEGER address INTEGER trapno END STRUCTURE STRUCTURE /siginfo/ INTEGER si_signo INTEGER si_code INTEGER si_errno RECORD /fault/ fault END STRUCTURE RECORD /siginfo/ sip location = sip.fault.address ... actions you take ... END
This f77 example would have to be modified to run on SPARC V9 architectures (-xarch=v9 or v9a) by replacing all INTEGER declarations within each STRUCTURE with INTEGER*8.
If the handler routine enabled by ieee_handler() is in Fortran as shown in the example, the routine should not make any reference to its first argument (sig). This first argument is passed by value to the routine and can only be referenced as loc(sig). The value is the signal number.
The following examples show how to create handler routines to detect floating-point exceptions.
Example: Detect exception and abort:
demo% cat DetExcHan.f EXTERNAL myhandler REAL r / 14.2 /, s / 0.0 / i = ieee_handler ('set', 'division', myhandler ) t = r/s END INTEGER FUNCTION myhandler(sig,code,context) INTEGER sig, code, context(5) CALL abort() END demo% f77 -silent DetExcHan.f demo% a.out abort: called Abort (core dumped) demo%
SIGFPE is generated whenever that floating-point exception occurs. When the SIGFPE is detected, control passes to the myhandler function, which immediately aborts. Compile with -g and use dbx to find the location of the exception.
Example: Locate an exception (print address) and abort:
demo% cat LocExcHan.F #include "f77_floatingpoint.h" EXTERNAL Exhandler INTEGER Exhandler, i, ieee_handler REAL r / 14.2 /, s / 0.0 /, t C Detect division by zero i = ieee_handler( 'set', 'division', Exhandler ) t = r/s END INTEGER FUNCTION Exhandler( sig, sip, uap) INTEGER sig STRUCTURE /fault/ INTEGER address END STRUCTURE STRUCTURE /siginfo/ INTEGER si_signo INTEGER si_code INTEGER si_errno RECORD /fault/ fault END STRUCTURE RECORD /siginfo/ sip WRITE (*,10) sip.si_signo, sip.si_code, sip.fault.address 10 FORMAT('Signal ',i4,' code ',i4,' at hex address ', Z8 ) CALL abort() END demo% f77 -silent -g LocExcHan.F demo% a.out Signal 8 code 3 at hex address 11230 abort: called Abort (core dumped) demo%
In SPARC V9 environments, replace the INTEGER declarations within each STRUCTURE with INTEGER*8, and the i4 formats with i8.
In most cases, knowing the actual address of the exception is of little use, except with dbx:
demo% dbx a.out (dbx) stopi at 0x11230 Set breakpoint at address (2) stopi at &MAIN+0x68 (dbx) run Run program Running: a.out (process id 18803) stopped in MAIN at 0x11230 MAIN+0x68: fdivs %f3, %f2, %f2 (dbx) where Shows the line number of the exception =>[1] MAIN(), line 7 in "LocExcHan.F" (dbx) list 7 Displays the source code line 7 t = r/s (dbx) cont Continue after breakpoint, enter handler routine Signal 8 code 3 at hex address 11230 abort: called signal ABRT (Abort) in _kill at 0xef6e18a4 _kill+0x8: bgeu _kill+0x30 Current function is exhandler 24 CALL abort() (dbx) quit demo%
Of course, there are easier ways to determine the source line that caused the error. However, this example does serve to show the basics of exception handling.
With f77, some system signal handlers for trapping interrupts, bus errors, segmentation violations, or illegal instructions are automatically enabled by default.
Although generally you would not want to turn off this default behavior, you can do so by compiling a C program that sets the global C variable f77_no_handlers to 1 and linking into your executable program:
demo% cat NoHandlers.c int f77_no_handlers=1 ; demo% cc -c NoHandlers.c demo% f77 NoHandlers.o MyProgram.f
Otherwise, by default, f77_no_handlers is 0. The setting takes effect just before execution is transferred to the user program.
This variable is in the global name space of the program; do not use f77_no_handlers as the name of a variable anywhere else in the program.
With f90, no signal handlers are on by default.
The ieee_retrospective function queries the floating-point status registers to find out which exceptions have accrued and a message is printed to standard error to inform you which exceptions were raised but not cleared. This function is automatically called by Fortran 77 programs at normal program termination (CALL EXIT). The message typically looks like this; the format may vary with each compiler release:
Note: IEEE floating-point exception flags raised: Division by Zero; IEEE floating-point exception traps enabled: inexact; underflow; overflow; invalid operation; See the Numerical Computation Guide, ieee_flags(3M), ieee_handler(3M)
Fortran 90 programs do not call ieee_retrospective automatically. A Fortran 90 program would need to call ieee_retrospective explicitly (and link with -lf77compat).