| Numerical Computation Guide |
Examples
This appendix provides examples of how to accomplish some popular tasks. The examples are written either in Fortran or ANSI C, and many depend on the current versions of
libmandlibsunmath. These examples were tested with the current C and Fortran compilers in the Solaris operating environment.IEEE Arithmetic
The following examples show one way you can examine the hexadecimal representations of floating-point numbers. Note that you can also use the debuggers to look at the hexadecimal representations of stored data.
The following C program prints a double precision approximation to
CODE EXAMPLE A-1and single precision infinity:
On SPARC, the output of the preceding program looks like this:
DP Approx pi = 400921fb 54442d18 = 3.14159265358979312e+00Single Precision Infinity: 7f800000The following Fortran program prints the smallest normal numbers in each format:
CODE EXAMPLE A-2 Print Smallest Normal Numbers in Each Format
On SPARC, the corresponding output reads:
(The Fortran compiler on x86 does not support the
real*16type. To run the preceding example on x86, delete thereal*16declaration and the code that calculates and prints the quadruple precision value.)The Math Libraries
This section shows examples that use functions from the math library.
Random Number Generator
The following example calls a random number generator to generate an array of numbers and uses a timing function to measure the time it takes to compute the
EXPof the given numbers:
To compile the preceding example, place the source code in a file with the suffix
F(notf) so that the compiler will automatically invoke the preprocessor, and specify either-DSPor-DDPon the command line to select single or double precision.This example shows how to use
d_addransto generate blocks of random data uniformly distributed over a user-specified range:
CODE EXAMPLE A-4 Using d_addrans
/* * test SIZE*LOOPS random arguments to sin in the range * [0, threshold] where * threshold = 3E30000000000000 (3.72529029846191406e-09) */ #include <math.h> #include <sunmath.h> #define SIZE 10000 #define LOOPS 100 int main(){ double x[SIZE], y[SIZE]; int i, j, n; double lb, ub;union { unsigned u[2]; double d; } upperbound; upperbound.u[0] = 0x3e300000; upperbound.u[1] = 0x00000000; /* initialize the random number generator */ d_init_addrans_(); /* test (SIZE * LOOPS) arguments to sin */for (j = 0; j < LOOPS; j++) { /* * generate a vector, x, of length SIZE, * of random numbers to use as * input to the trig functions. */ n = SIZE; ub = upperbound.d; lb = 0.0; d_addrans_(x, &n, &lb, &ub); for (i = 0; i < n; i++) y[i] = sin(x[i]); /* is sin(x) == x? It ought to, for tiny x. */ for (i = 0; i < n; i++) if (x[i] != y[i]) printf( " OOPS: %d sin(%18.17e)=%18.17e \n", i, x[i], y[i]); }printf(" comparison ended; no differences\n"); ieee_retrospective_(); return 0; }IEEE Recommended Functions
This Fortran example uses some functions recommended by the IEEE standard:
CODE EXAMPLE A-5 IEEE Recommended Functions
c c Demonstrate how to call 5 of the more interesting IEEE c recommended functions from fortran. These are implemented c with "bit-twiddling", and so are as efficient as you could c hope. The IEEE standard for floating-point arithmetic c doesn't require these, but recommends that they be c included in any IEEE programming environment. c c For example, to accomplish c y = x * 2**n, c since the hardware stores numbers in base 2, c shift the exponent by n places. c c Refer to c c ieee_functions(3m) c libm_double(3f) c libm_single(3f) c c The 5 functions demonstrated here are: c c ilogb(x): returns the base 2 unbiased exponent of x in c integer format c signbit(x): returns the sign bit, 0 or 1 c copysign(x,y): returns x with y's sign bit c nextafter(x,y): next representable number after x, in c the direction y c scalbn(x,n): x * 2**n c c function double precision single precision c -------------------------------------------------------- c ilogb(x) i = id_ilogb(x) i = ir_ilogb(r) c signbit(x) i = id_signbit(x) i = ir_signbit(r) c copysign(x,y) x = d_copysign(x,y) r = r_copysign(r,s) c nextafter(x,y) z = d_nextafter(x,y) r = r_nextafter(r,s) c scalbn(x,n) x = d_scalbn(x,n) r = r_scalbn(r,n) c program ieee_functions_demo implicit double precision (d) implicit real (r) double precision x, y, z, direction real r, s, t, r_direction integer i, scale print * print *, 'DOUBLE PRECISION EXAMPLES:' print * x = 32.0d0 i = id_ilogb(x) write(*,1) x, i1 format(' The base 2 exponent of ', F4.1, ' is ', I2) x = -5.5d0 y = 12.4d0 z = d_copysign(x,y) write(*,2) x, y, z 2 format(F5.1, ' was given the sign of ', F4.1, * ' and is now ', F4.1) x = -5.5d0 i = id_signbit(x) print *, 'The sign bit of ', x, ' is ', i x = d_min_subnormal() direction = -d_infinity() y = d_nextafter(x, direction) write(*,3) x3 format(' Starting from ', 1PE23.16E3, - ', the next representable number ') write(*,4) direction, y4 format(' towards ', F4.1, ' is ', 1PE23.16E3) x = d_min_subnormal() direction = 1.0d0 y = d_nextafter(x, direction) write(*,3) x write(*,4) direction, y x = 2.0d0 scale = 3 y = d_scalbn(x, scale) write (*,5) x, scale, y5 format(' Scaling ', F4.1, ' by 2**', I1, ' is ', F4.1) print * print *, 'SINGLE PRECISION EXAMPLES:' print * r = 32.0 i = ir_ilogb(r) write (*,1) r, i r = -5.5 i = ir_signbit(r) print *, 'The sign bit of ', r, ' is ', i r = -5.5 s = 12.4 t = r_copysign(r,s) write (*,2) r, s, t r = r_min_subnormal() r_direction = -r_infinity() s = r_nextafter(r, r_direction) write(*,3) r write(*,4) r_direction, s r = r_min_subnormal() r_direction = 1.0e0 s = r_nextafter(r, r_direction) write(*,3) r write(*,4) r_direction, s r = 2.0 scale = 3 s = r_scalbn(r, scale) write (*,5) r, scale, y print * endThe output from this program is shown in CODE EXAMPLE A-6.
CODE EXAMPLE A-6 Output of CODE EXAMPLE A-5
The base 2 exponent of 32.0 is 5 -5.5 was given the sign of 12.4 and is now 5.5 The sign bit of -5.5000000000000 is 1 Starting from 4.9406564584124654E-324, the next representable number towards -Inf is 0.0000000000000000E+000 Starting from 4.9406564584124654E-324, the next representable number towards 1.0 is 9.8813129168249309E-324 Scaling 2.0 by 2**3 is 16.0 SINGLE PRECISION EXAMPLES: The base 2 exponent of 32.0 is 5 The sign bit of -5.50000 is 1 -5.5 was given the sign of 12.4 and is now 5.5 Starting from 1.4012984643248171E-045, the next representable number towards -Inf is 0.0000000000000000E+000 Starting from 1.4012984643248171E-045, the next representable number towards 1.0 is 2.8025969286496341E-045 Scaling 2.0 by 2**3 is 16.0 Note:IEEE floating-point exception flags raised: Inexact; Underflow; See the Numerical Computation Guide, ieee_flags(3M)IEEE Special Values
This C program calls several of the
ieee_values(3m) functions:
#include <math.h>#include <sunmath.h>int main(){double x;float r;x = quiet_nan(0);printf("quiet NaN: %.16e = %08x %08x \n",x, ((int *) &x)[0], ((int *) &x)[1]);x = nextafter(max_subnormal(), 0.0);printf("nextafter(max_subnormal,0) = %.16e\n",x);printf(" = %08x %08x\n",((int *) &x)[0], ((int *) &x)[1]);r = min_subnormalf();printf("single precision min subnormal = %.8e = %08x\n",r, ((int *) &r)[0]);return 0;}Remember to specify both
-lsunmathand-lmwhen linking.On SPARC, the output looks like this:
quiet NaN: NaN =7fffffffffffffffnextafter(max_subnormal,0) = 2.2250738585072004e-308=000ffffffffffffesingle precision min subnormal = 1.40129846e-45 = 00000001Because the x86 architecture is "little-endian", the output on x86 is slightly different (the high and low order words of the hexadecimal representations of the double precision numbers are reversed):
quiet NaN: NaN = ffffffff 7fffffffnextafter(max_subnormal,0) = 2.2250738585072004e-308= fffffffe000fffffsingle precision min subnormal = 1.40129846e-45 = 00000001Fortran programs that use
ieee_valuesfunctions should take care to declare those functions' types:
program print_ieee_valuescc the purpose of the implicit statements is to insurec that the f77_floatingpoint pseudo-instrinsicc functions are declared with the correct typecimplicit real*16 (q)implicit double precision (d)implicit real (r)real*16 z, zero, onedouble precision xreal rczero = 0.0one = 1.0z = q_nextafter(zero, one)x = d_infinity()r = r_max_normal()cprint *, zprint *, xprint *, rcendOn SPARC, the output reads as follows:
6.4751751194380251109244389582276466-4966Infinity3.40282E+38(Recall that the Fortran compiler on x86 does not support the
real*16type. To run the preceding example on x86, delete all references toreal*16variables and functions.)
ieee_flags-- Rounding DirectionThe following example demonstrates how to set the rounding mode to round towards zero:
#include <math.h>#include <sunmath.h>int main(){int i;double x, y;char *out_1, *out_2, *dummy;/* get prevailing rounding direction */i = ieee_flags("get", "direction", "", &out_1);x = sqrt(.5);printf("With rounding direction %s, \n", out_1);printf("sqrt(.5) = 0x%08x 0x%08x = %16.15e\n",((int *) &x)[0], ((int *) &x)[1], x);/* set rounding direction */if (ieee_flags("set", "direction", "tozero", &dummy) != 0)printf("Not able to change rounding direction!\n");i = ieee_flags("get", "direction", "", &out_2);x = sqrt(.5);/** restore original rounding direction before printf, since* printf is also affected by the current rounding direction*/if (ieee_flags("set", "direction", out_1, &dummy) != 0)printf("Not able to change rounding direction!\n");printf("\nWith rounding direction %s,\n", out_2);printf("sqrt(.5) = 0x%08x 0x%08x = %16.15e\n",((int *) &x)[0], ((int *) &x)[1], x);return 0;}(SPARC) The output of this short program shows the effects of rounding towards zero:
demo% cc rounding_direction.c -lsunmath -lmdemo% a.outWith rounding direction nearest,sqrt(.5) =0x3fe6a09e0x667f3bcd = 7.071067811865476e-01With rounding direction tozero,sqrt(.5) = 0x3fe6a09e 0x667f3bcc = 7.071067811865475e-01demo%(x86) The output of this short program shows the effects of rounding towards zero:
demo% cc rounding_direction.c -lsunmath -lmdemo% a.outWith rounding direction nearest,sqrt(.5) = 0x667f3bcd 0x3fe6a09e = 7.071067811865476e-01With rounding direction tozero,sqrt(.5) = 0x667f3bcc 0x3fe6a09e = 7.071067811865475e-01demo%To set rounding direction towards zero from a Fortran program:
program ieee_flags_democharacter*16 outi = ieee_flags('set', 'direction', 'tozero', out)if (i.ne.0) print *, 'not able to set rounding direction'i = ieee_flags('get', 'direction', '', out)print *, 'Rounding direction is: ', outendThe output is as follows:
Rounding direction is: tozeroNote: Rounding direction toward zero.See the Numerical Computation Guide, ieee_flags(3M)C99 Floating Point Environment Functions
The next example illustrates the use of several of the C99 floating point environment functions. The
.normfunction computes the Euclidean norm of a vector and uses the environment functions to handle underflow and overflow. The main program calls this function with vectors that are scaled to ensure that underflows and overflows occur, as the retrospective diagnostic output shows
CODE EXAMPLE A-7 C99 Floating Point Environment Functions
#include <stdio.h> #include <math.h> #include <sunmath.h> #include <fenv.h> /* * Compute the euclidean norm of the vector x avoiding * premature underflow or overflow */ double norm(int n, double *x){ fenv_t env; double s, b, d, t; int i, f; /* save the environment, clear flags, and establish nonstop exception handling */ feholdexcept(&env); /* attempt to compute the dot product x.x */ d = 1.0; /* scale factor */ s = 0.0; for (i = 0; i < n; i++) s += x[i] * x[i]; /* check for underflow or overflow */ f = fetestexcept(FE_UNDERFLOW | FE_OVERFLOW);if (f & FE_OVERFLOW) { /* first attempt overflowed, try again scaling down */ feclearexcept(FE_OVERFLOW); b = scalbn(1.0, -640); d = 1.0 / b; s = 0.0;for (i = 0; i < n; i++) { t = b * x[i]; s += t * t; } }else if (f & FE_UNDERFLOW && s < scalbn(1.0, -970)) { /* first attempt underflowed, try again scaling up */ b = scalbn(1.0, 1022); d = 1.0 / b; s = 0.0;for (i = 0; i < n; i++) { t = b * x[i]; s += t * t; } } /* hide any underflows that have occurred so far */ feclearexcept(FE_UNDERFLOW); /* restore the environment, raising any other exceptions that have occurred */ feupdateenv(&env); /* take the square root and undo any scaling */ return d * sqrt(s); } int main(){ double x[100], l, u; int n = 100; fex_set_log(stdout); l = 0.0; u = min_normal(); d_lcrans_(x, &n, &l, &u);printf("norm: %g\n", norm(n, x)); l = sqrt(max_normal()); u = l * 2.0; d_lcrans_(x, &n, &l, &u);printf("norm: %g\n", norm(n, x)); return 0; }On SPARC, compiling and running this program produces the following:
demo%cc norm.c -R/opt/SUNWspro/lib -L/opt/SUNWspro/lib -lm9x
-lsunmath -lmdemo%a.outFloating point underflow at 0x000153a8 __d_lcrans_, nonstop mode0x000153b4 __d_lcrans_0x00011594 mainFloating point underflow at 0x00011244 norm, nonstop mode0x00011248 norm0x000115b4 mainnorm: 1.32533e-307Floating point overflow at 0x00011244 norm, nonstop mode0x00011248 norm0x00011660 mainnorm: 2.02548e+155The following example shows the effect of the
fesetprecfunction on x86. (This function is not available on SPARC.) Thewhileloops attempt to determine the available precision by finding the largest power of two that rounds off entirely when it is added to one. As the first loop shows, this technique does not always work as expected on architectures like x86 that evaluate all intermediate results in extended precision. Thus, thefesetprecfunction may be used to guarantee that all results will be rounded to the desired precision, as the second loop shows.
CODE EXAMPLE A-8 fesetprecfunction (x86)
#include <math.h> #include <fenv.h> int main(){ double x; x = 1.0; while (1.0 + x != 1.0) x *= 0.5;printf("%d significant bits\n", -ilogb(x)); fesetprec(FE_DBLPREC); x = 1.0; while (1.0 + x != 1.0) x *= 0.5;printf("%d significant bits\n", -ilogb(x)); return 0; }The output from this program on x86 systems is:
64 significant bits53 significant bitsFinally, the following code fragment shows one way to use the environment functions in a multi-threaded program to propagate floating point modes from a parent thread to a child thread and recover exception flags raised in the child thread when it joins with the parent. (See the Solaris Multithreaded Programming Guide for more information on writing multi-threaded programs.)
CODE EXAMPLE A-9 Using Environment Functions in Multi-Thread Program
#include <thread.h> #include <fenv.h> fenv_t env; void child(void *p){ /* inherit the parent's environment on entry */ fesetenv(&env); ... /* save the child's environment before exit */ fegetenv(&env); } void parent(){ thread_t tid; void *arg; ... /* save the parent's environment before creating the child */ fegetenv(&env); thr_create(NULL, NULL, child, arg, NULL, &tid); ... /* join with the child */ thr_join(tid, NULL, &arg); /* merge exception flags raised in the child into the parent's environment */ fex_merge_flags(&env); ... }Exceptions and Exception Handling
ieee_flags-- Accrued Exceptions
Generally, a user program examines or clears the accrued exception bits. Here is a C program that examines the accrued exception flags:
CODE EXAMPLE A-10 Examining the Accrued Exception Flags
#include <sunmath.h> #include <sys/ieeefp.h> int main(){ int code, inexact, division, underflow, overflow, invalid; double x; char *out; /* cause an underflow exception */ x = max_subnormal() / 2.0; /* this statement insures that the previous */ /* statement is not optimized away */printf("x = %g\n",x); /* find out which exceptions are raised */code = ieee_flags("get", "exception", "", &out); /* decode the return value */ inexact = (code >> fp_inexact) & 0x1; underflow = (code >> fp_underflow) & 0x1; division = (code >> fp_division) & 0x1; overflow = (code >> fp_overflow) & 0x1; invalid = (code >> fp_invalid) & 0x1; /* "out" is the raised exception with the highest priority */printf(" Highest priority exception is: %s\n", out); /* The value 1 means the exception is raised, */ /* 0 means it isn't. */printf("%d %d %d %d %d\n", invalid, overflow, division, underflow, inexact); ieee_retrospective_(); return 0; }The output from running this program:
demo% a.outx = 1.11254e-308Highest priority exception is: underflow0 0 0 1 1Note:IEEE floating-point exception flags raised:Inexact; Underflow;See the Numerical Computation Guide, ieee_flags(3M)The same can be done from Fortran:
CODE EXAMPLE A-11 Examining the Accrued Exception Flags - Fortran
/* A Fortran example that: * causes an underflow exception * uses ieee_flags to determine which exceptions are raised * decodes the integer value returned by ieee_flags * clears all outstanding exceptions Remember to save this program in a file with the suffix .F, so that the c preprocessor is invoked to bring in the header file f77_floatingpoint.h. */ #include <f77_floatingpoint.h> program decode_accrued_exceptions double precision x integer accrued, inx, div, under, over, inv character*16 out double precision d_max_subnormal c Cause an underflow exception x = d_max_subnormal() / 2.0 c Find out which exceptions are raisedaccrued = ieee_flags('get', 'exception', '', out) c Decode value returned by ieee_flags using bit-shift intrinsics inx = and(rshift(accrued, fp_inexact) , 1) under = and(rshift(accrued, fp_underflow), 1) div = and(rshift(accrued, fp_division) , 1) over = and(rshift(accrued, fp_overflow) , 1) inv = and(rshift(accrued, fp_invalid) , 1) c The exception with the highest priority is returned in "out" print *, "Highest priority exception is ", out c The value 1 means the exception is raised; 0 means it is not print *, inv, over, div, under, inx c Clear all outstanding exceptionsi = ieee_flags('clear', 'exception', 'all', out) endThe output is as follows:
Highest priority exception is underflow0 0 0 1 1While it is unusual for a user program to set exception flags, it can be done. This is demonstrated in the following C example.
#include <sunmath.h>int main(){int code;char *out;if (ieee_flags("clear", "exception", "all", &out) != 0)printf("could not clear exceptions\n");if (ieee_flags("set", "exception", "division", &out) != 0)printf("could not set exception\n");code = ieee_flags("get", "exception", "", &out);printf("out is: %s , fp exception code is: %X \n",out, code);return 0;}On SPARC, the output from the preceding program is:
out is: division , fp exception code is: 2On x86, the output is:
out is: division , fp exception code is: 4
ieee_handler-- Trapping Exceptions
Note The examples below apply only to the Solaris operating environment.
Here is a Fortran program that installs a signal handler to locate an exception (for SPARC systems only):
CODE EXAMPLE A-12 Trap on Underflow - SPARC
program demo c declare signal handler function external fp_exc_hdl double precision d_min_normal double precision x c set up signal handleri = ieee_handler('set', 'common', fp_exc_hdl) if (i.ne.0) print *, 'ieee trapping not supported here' c cause an underflow exception (it will not be trapped) x = d_min_normal() / 13.0 print *, 'd_min_normal() / 13.0 = ', x c cause an overflow exception c the value printed out is unrelated to the result x = 1.0d300*1.0d300 print *, '1.0d300*1.0d300 = ', x end c c the floating-point exception handling function c integer function fp_exc_hdl(sig, sip, uap) integer sig, code, addr character label*16 c c The structure /siginfo/ is a translation of siginfo_t c from <sys/siginfo.h> c 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 c See <sys/machsig.h> for list of FPE codes c Figure out the name of the SIGFPE code = sip.si_code if (code.eq.3) label = 'division' if (code.eq.4) label = 'overflow' if (code.eq.5) label = 'underflow' if (code.eq.6) label = 'inexact' if (code.eq.7) label = 'invalid' addr = sip.fault.address c Print information about the signal that happened write (*,77) code, label, addr77 format ('floating-point exception code ', i2, ',', * a17, ',', ' at address ', z8 ) endThe output is:
d_min_normal() / 13.0 = 1.7115952757748-309floating-point exception code 4, overflow , at address 1131C1.0d300*1.0d300 = 1.0000000000000+300Note: IEEE floating-point exception flags raised:Inexact; Underflow;IEEE floating-point exception traps enabled:overflow; division by zero; invalid operation;See the Numerical Computation Guide, ieee_flags(3M),
ieee_handler(3M)
(SPARC) Here is a more complex C example:
CODE EXAMPLE A-13 Trap on Invalid, Division by 0, Overflow, Underflow, and Inexact - SPARC
/* * Generate the 5 IEEE exceptions: invalid, division, * overflow, underflow and inexact. ** Trap on any floating point exception, print a message, * and continue. ** Note that you could also inquire about raised exceptions by* i = ieee("get","exception","",&out); * where out contains the name of the highest exception * raised, and i can be decoded to find out about all the * exceptions raised. */ #include <sunmath.h> #include <signal.h> #include <siginfo.h> #include <ucontext.h> extern void trap_all_fp_exc(int sig, siginfo_t *sip, ucontext_t *uap); int main(){ double x, y, z; char *out; /* * Use ieee_handler to establish "trap_all_fp_exc" * as the signal handler to use whenever any floating * point exception occurs. */if (ieee_handler("set", "all", trap_all_fp_exc) != 0)printf(" IEEE trapping not supported here.\n"); /* disable trapping (uninteresting) inexact exceptions */if (ieee_handler("set", "inexact", SIGFPE_IGNORE) != 0)printf("Trap handler for inexact not cleared.\n"); /* raise invalid */if (ieee_flags("clear", "exception", "all", &out) != 0)printf(" could not clear exceptions\n");printf("1. Invalid: signaling_nan(0) * 2.5\n"); x = signaling_nan(0); y = 2.5; z = x * y; /* raise division */if (ieee_flags("clear", "exception", "all", &out) != 0)printf(" could not clear exceptions\n");printf("2. Div0: 1.0 / 0.0\n"); x = 1.0; y = 0.0; z = x / y; /* raise overflow */if (ieee_flags("clear", "exception", "all", &out) != 0)printf(" could not clear exceptions\n");printf("3. Overflow: -max_normal() - 1.0e294\n"); x = -max_normal(); y = -1.0e294; z = x + y; /* raise underflow */if (ieee_flags("clear", "exception", "all", &out) != 0)printf(" could not clear exceptions\n");printf("4. Underflow: min_normal() * min_normal()\n"); x = min_normal(); y = x; z = x * y; /* enable trapping on inexact exception */if (ieee_handler("set", "inexact", trap_all_fp_exc) != 0)printf("Could not set trap handler for inexact.\n"); /* raise inexact */if (ieee_flags("clear", "exception", "all", &out) != 0)printf(" could not clear exceptions\n");printf("5. Inexact: 2.0 / 3.0\n"); x = 2.0; y = 3.0; z = x / y; /* don't trap on inexact */if (ieee_handler("set", "inexact", SIGFPE_IGNORE) != 0)printf(" could not reset inexact trap\n"); /* check that we're not trapping on inexact anymore */if (ieee_flags("clear", "exception", "all", &out) != 0)printf(" could not clear exceptions\n");printf("6. Inexact trapping disabled; 2.0 / 3.0\n"); x = 2.0; y = 3.0; z = x / y; /* find out if there are any outstanding exceptions */ ieee_retrospective_(); /* exit gracefully */ return 0; }void trap_all_fp_exc(int sig, siginfo_t *sip, ucontext_t *uap) { char *label = "undefined"; /* see /usr/include/sys/machsig.h for SIGFPE codes */switch (sip->si_code) { case FPE_FLTRES: label = "inexact"; break; case FPE_FLTDIV: label = "division"; break; case FPE_FLTUND: label = "underflow"; break; case FPE_FLTINV: label = "invalid"; break; case FPE_FLTOVF: label = "overflow"; break; } printf( " signal %d, sigfpe code %d: %s exception at address %x\n", sig, sip->si_code, label, sip->_data._fault._addr); }The output is similar to the following:
1. Invalid: signaling_nan(0) * 2.5signal 8, sigfpe code 7: invalid exception at address 10da82. Div0: 1.0 / 0.0signal 8, sigfpe code 3: division exception at address 10e443. Overflow: -max_normal() - 1.0e294signal 8, sigfpe code 4: overflow exception at address 10ee84. Underflow: min_normal() * min_normal()signal 8, sigfpe code 5: underflow exception at address 10f805. Inexact: 2.0 / 3.0signal 8, sigfpe code 6: inexact exception at address 1106c6. Inexact trapping disabled; 2.0 / 3.0Note: IEEE floating-point exception traps enabled:underflow; overflow; division by zero; invalid operation;See the Numerical Computation Guide, ieee_handler(3M)(SPARC) The following program shows how you can use
ieee_handlerand the include files to modify the default result of certain exceptional situations:
CODE EXAMPLE A-14 Modifying the Default Result of Exceptional Situations
/* * Cause a division by zero exception and use the * signal handler to substitute MAXDOUBLE (or MAXFLOAT) * as the result. * * compile with the flag -Xa */ #include <values.h> #include <siginfo.h> #include <ucontext.h> void division_handler(int sig, siginfo_t *sip, ucontext_t *uap);int main() { double x, y, z; float r, s, t; char *out; /* * Use ieee_handler to establish division_handler as the * signal handler to use for the IEEE exception division. */if (ieee_handler("set","division",division_handler)!=0) {printf(" IEEE trapping not supported here.\n"); } /* Cause a division-by-zero exception */ x = 1.0; y = 0.0; z = x / y; /* * Check to see that the user-supplied value, MAXDOUBLE, * is indeed substituted in place of the IEEE default * value, infinity. */printf("double precision division: %g/%g = %g \n",x,y,z); /* Cause a division-by-zero exception */ r = 1.0; s = 0.0; t = r / s; /* * Check to see that the user-supplied value, MAXFLOAT, * is indeed substituted in place of the IEEE default * value, infinity. */printf("single precision division: %g/%g = %g \n",r,s,t); ieee_retrospective_(); return 0; }void division_handler(int sig, siginfo_t *sip, ucontext_t *uap) { int inst; unsigned rd, mask, single_prec=0; float f_val = MAXFLOAT; double d_val = MAXDOUBLE; long *f_val_p = (long *) &f_val; /* Get instruction that caused exception. */ inst = uap->uc_mcontext.fpregs.fpu_q->FQu.fpq.fpq_instr; /* * Decode the destination register. Bits 29:25 encode the * destination register for any SPARC floating point * instruction. */ mask = 0x1f; rd = (mask & (inst >> 25)); /* * Is this a single precision or double precision * instruction? Bits 5:6 encode the precision of the * opcode; if bit 5 is 1, it's sp, else, dp. */ mask = 0x1; single_prec = (mask & (inst >> 5)); /* put user-defined value into destination register */if (single_prec) { uap->uc_mcontext.fpregs.fpu_fr.fpu_regs[rd] = f_val_p[0];} else { uap->uc_mcontext.fpregs.fpu_fr.fpu_dregs[rd/2] = d_val; } }As expected, the output is:
double precision division: 1/0 = 1.79769e+308single precision division: 1/0 = 3.40282e+38Note: IEEE floating-point exception traps enabled:division by zero;See the Numerical Computation Guide, ieee_handler(3M)
ieee_handler-- Abort on ExceptionsYou can use
ieee_handlerto force a program to abort in case of certain floating-point exceptions:
#include <f77_floatingpoint.h>program abortcieeer = ieee_handler('set', 'division', SIGFPE_ABORT)if (ieeer .ne. 0) print *, ' ieee trapping not supported'r = 14.2s = 0.0r = r/scprint *, 'you should not see this; system should abort'cend
libm9x.soException Handling FeaturesThe following examples show how to use some of the exception handling features provided by
libm9x.so. The first example is based on the following task: given a number x and coefficients a0, a1,..., aN, and b0, b1,..., bN-1, evaluate the function f(x) and its first derivative f'(x), where f is the continued fraction
- f(x) = a0 + b0/(x + a1 + b1/(x + ... /(x + aN-1 + bN-1/(x + aN))...).
Computing f is straightforward in IEEE arithmetic: even if one of the intermediate divisions overflows or divides by zero, the default value specified by the standard (a correctly signed infinity) turns out to yield the correct result. Computing f', on the other hand, can be more difficult because the simplest form for evaluating it can have removable singularities. If the computation encounters one of these singularities, it will attempt to evaluate one of the indeterminate forms 0/0, 0*infinity, or infinity/infinity, all of which raise invalid operation exceptions. W. Kahan has proposed a method for handling these exceptions via a feature called "presubstitution".
Presubstitution is an extension of the IEEE default response to exceptions that lets the user specify in advance the value to be substituted for the result of an exceptional operation. Using
libm9x.so, a program can implement presubstitution easily by installing a handler in theFEX_CUSTOMexception handling mode. This mode allows the handler to supply any value for the result of an exceptional operation simply by storing that value in the data structure pointed to by the info parameter passed to the handler. Here is a sample program to compute the continued fraction and its derivative using presubstitution implemented with aFEX_CUSTOMhandler.
CODE EXAMPLE A-15 Computing the Continued Fraction and its Derivative Using the FEX_CUSTOMHandler
#include <stdio.h> #include <sunmath.h> #include <fenv.h> volatile double p; void handler(int ex, fex_info_t *info){