ieee_handler(3M)
NAME
ieee_handler − IEEE exception trap handler function
SYNOPSIS
#include <floatingpoint.h>
int ieee_handler(action,exception,hdl)
char action[], exception[];
sigfpe_handler_type hdl;
DESCRIPTION
This function provides easy exception handling to exploit ANSI/IEEE Std 754-1985 arithmetic in a C program. The first two arguments are pointers to strings. For efficiency, results arising from invalid arguments and invalid combinations are undefined.
There are three types of action: “get”, “set”, and “clear”. There are five types of exception:
“inexact”
“division” ... division by zero exception
“underflow”
“overflow”
“invalid”
“all” ... all five exceptions above
“common” ... invalid, overflow, and division exceptions
Note: “all” and “common” only make sense with “set” or “clear”.
hdl contains the address of a signal-handling routine. <floatingpoint.h> defines sigfpe_handler_type .
“get” will return the location of the current handler routine for exception cast to an int. “set” will set the routine pointed at by hdl to be the handler routine and at the same time enable the trap on exception, except when hdl == SIGFPE_DEFAULT or SIGFPE_IGNORE; then ieee_handler() will disable the trap on exception. When hdl == SIGFPE_ABORT, any trap on exception will dump core using abort(3C). “clear” “all” disables trapping on all five exceptions.
Two steps are required to intercept an IEEE-related SIGFPE code with ieee_handler:
1) Set up a handler with ieee_handler.
2) Perform a floating-point operation that generates the intended IEEE exception.
ieee_handler() also adjusts floating-point hardware mode bits affecting IEEE trapping. For “clear”, “set” SIGFPE_DEFAULT, or “set” SIGFPE_IGNORE, the hardware trap is disabled. For any other “set”, the hardware trap is enabled.
SIGFPE signals can be handled using sigaction(2) or ieee_handler(3M).
In a particular program, to avoid confusion, use only one of these interfaces to handle SIGFPE signals.
BUGS
If multiple FP exceptions occur, the unfinished instructions line up in the FP Queue. After the top instruction is taken care of (by a signal handler), the kernel should pop the queue so that the next instruction is available for trapping. Currently the kernel is not doing that. That means a signal handler has to deal with the FP Queue (either zero the qcnt to tell the kernel nothing is in the queue or pop one entry off the queue by incrementing the q pointer and decrementing qcnt) or it will loop indefinitely after it gets the signal. ieee_handler fixes this problem by popping off the top entry of the FP Queue of exceptions, so that the next trapped instruction if any will be handled correctly. Users of sigaction will have to handle the FP Queue themselves.
DIAGNOSTICS
ieee_handler() normally returns 0 for “set”; 1 will be returned if the action is not available (for instance, not supported in hardware). For “get”, the address of the current handler is returned, cast to an int.
EXAMPLE
Here we give an example of how to trap an invalid signal and change the default output (NaN) to a user given value if the exception is caused by zero/zero. Following is a user-specified signal handler:
/∗
∗ Sample user exception handler routine. In this example, we trap on the
∗ invalid signal. Then we check if the exception is of zero/zero type. If
∗ yes, we set the result = zero_over_zero_value (user given).
∗/
#include <floatingpoint.h>
#include <siginfo.h>
#include <ucontext.h>
extern double zero_over_zero_value;
void zero_over_zero_handler(sig, sip, uap)
intsig;
siginfo_t∗sip;
ucontext_t∗uap;
{
/∗ see <sys/reg.h> for structure fpregset_t ∗/
fpregset_t ∗uc = &(uap->uc_mcontext.fpregs);
long i, j, k, fop, frd, frs1, frs2, fopshift = 5,
frdshift = 25, frs1shift = 14, frs2shift = 0;
long ∗con = (long ∗) (&zero_over_zero_value);
/∗
∗ find out registers rd, rs1, rs2, and opf
∗/
fop = ((uc->fpu_q->FQu.fpq.fpq_instr)>>fopshift) &0x1ff;
frd = ((uc->fpu_q->FQu.fpq.fpq_instr)>>frdshift) &0x1f;
frs1= ((uc->fpu_q->FQu.fpq.fpq_instr)>>frs1shift)&0x1f;
frs2= ((uc->fpu_q->FQu.fpq.fpq_instr)>>frs2shift )&0x1f;
/∗
∗ check if both rs1 and rs2 are zero (0/0 case)
∗/
i = (uc->fpu_fr.fpu_regs[frs2]&0x7fffffff)|uc->fpu_fr.fpu_regs[frs2+1];
j = (uc->fpu_fr.fpu_regs[frs1]&0x7fffffff)|uc->fpu_fr.fpu_regs[frs1+1];
switch (fop) {
case 0x4e:/∗ fdivd ∗/
if((i|j)==0) {/∗ 0/0 , set rd to be zero_over_zero_value ∗/
uc->fpu_fr.fpu_regs[frd] = con[0];
uc->fpu_fr.fpu_regs[frd+1] = con[1];
}
break;
}
}
and it might be set up like this:
#include <floatingpoint.h>
#include <siginfo.h>
#include <ucontext.h>
extern void zero_over_zero_handler();
double zero_over_zero_value;
main()
{
double x,w,sin();
int i,j,k;
sigfpe_handler_type hdl, old_handler1;
/∗
∗ save current invalid handlers
∗/
old_handler1 = (sigfpe_handler_type) ieee_handler("get","invalid", hdl);
/∗
∗ set up new invalid handler
∗/
hdl = (sigfpe_handler_type) zero_over_zero_handler;
ieee_handler("set","invalid",hdl);
/∗
∗ compute (k∗x)/sin(x) for k=2, x=0.5, 0.4, ..., 0.1, 0.0
∗/
k = 2.0;/∗ user specified ∗/
printf("Evaluating f(x) = (k∗x)/sin(x)\n\n");
zero_over_zero_value = k;
for (i= 5;i>=0;i--) {
x = i∗0.1;
w = (k∗x)/sin(x);
printf("\tx=%3.3f\t f(x) = % 1.20e\n",x,w);
}
/∗
∗ restore old invalid handlers
∗/
ieee_handler("set","invalid", old_handler1);
exit(0);
}
Here is what the output look like:
Evaluating f(x) = (k∗x)/sin(x)
x=0.500 f(x) = 2.08582964293348816000e+00
x=0.400 f(x) = 2.05434596443822626000e+00
x=0.300 f(x) = 2.03031801709447368000e+00
x=0.200 f(x) = 2.01339581906893761000e+00
x=0.100 f(x) = 2.00333722632695554000e+00
x=0.000 f(x) = 2.00000000000000000000e+00
Note that when x=0, f(x) = 0/0 and an invalid exception occurs. The value of 0/0 is set to be 2.0 in this example.
FILES
/usr/include/floatingpoint.h
/usr/include/signal.h
/usr/include/siginfo.h
/usr/include/ucontext.h
/usr/lib/libm.a
SEE ALSO
sigaction(2), abort(3C), signal(2), siginfo(5), signal(5), ucontext(5)
SunOS 5.1/SPARC — Last change: 24 Jan 1992