cc [ flag ... ] file ... -lsunmath -lm [ library ... ] #include <sunmath.h> long ieee_handler(const char *action, const char *exception, sigfpe_handler_type hdl);
此函数同时为 ANSI/IEEE Std 754-1985 定义的五个浮点异常建立 SIGFPE 信号处理程序并控制浮点陷阱启用模式。action 和 exception 参数是指定要执行的操作的不区分大小写的字符串的指针。无效的参数字符串和无效参数组合会产生不确定的结果。
action 有三个有效值:“get”、“set” 和 “clear”,exception 有七个有效值:
“inexact” “division” ... division by zero exception “underflow” “overflow” “invalid” “all” ... all five exceptions above “common” ... invalid, overflow, and division exceptions
如果 action 为 “set”,ieee_handler() 将为指定的异常建立处理程序 hdl。hdl 可以是 SIGFPE_DEFAULT、SIGFPE_IGNORE 或 SIGFPE_ABORT 之一,或者是用户定义的 SIGFPE 处理程序的地址。(宏 SIGFPE_DEFAULT、SIGFPE_IGNORE 和 SIGFPE_ABORT 以及类型 sigfpe_handler_type 在 <floatingpoint.h> 中定义。)
如果 hdl 是 SIGFPE_DEFAULT 或 SIGFPE_IGNORE,则 ieee_handler() 将对指定的异常禁用捕获,从而后续发生的这些异常会导致 IEEE 754 指定的缺省行为。
如果 hdl 是 SIGFPE_ABORT,则 ieee_handler() 将对指定异常启用捕获;后续发生这些异常将导致程序通过调用 abort(3c) 转储信息。
如果 hdl 是用户定义的 SIGFPE 处理程序的地址,则 ieee_handler() 将对指定的异常启用捕获,后续发生这些异常将导致调用处理程序,就像通过 sigaction(2) 安装了处理程序并设置了 SA_SIGINFO 标志一样。
如果 action 是 “clear”,则 ieee_handler() 将对指定的异常禁用捕获,并将对应的处理程序设置为 SIGFPE_DEFAULT。
如果 action 为 “get”,则 ieee_handler() 将返回与指定异常关联的处理程序(强制转换为类型 long)。忽略 hdl 参数。
对于非 “get” 操作,ieee_handler() 在请求的操作不可用(例如,底层硬件不支持对指定的异常启用或禁用捕获)时返回 1,否则返回 0。
要使用 ieee_handler() 捕获浮点异常导致的 SIGFPE 信号,程序必须:
使用 ieee_handler 设置处理程序。
执行生成预期意外的浮点操作。
以下程序说明了为无效操作异常设置处理程序并通过尝试计算 0/0 触发该异常的一种方式。然后,处理程序替代 IEEE 754 为此操作指定的缺省结果的某个全局变量的值。请注意,下面显示的处理程序仅在为 SPARC V8 指令集编译程序时对 SPARC 系统有效。
/* * Sample floating point exception handler. In this example, we trap * on the invalid operation exception. The handler checks that the * exception occurred as a result of an attempt to compute 0/0. If so, * it supplies the value of the global variable zero_over_zero_value * for the result of the instruction that raised the exception. */ #include <stdio.h> #include <stdlib.h> #include <math.h> #include <sunmath.h> #include <siginfo.h> #include <ucontext.h> double zero_over_zero_value; void zero_over_zero_handler(int sig, siginfo_t *sip, ucontext_t *uap) { /* see <sys/reg.h> for structure fpregset_t */ fpregset_t *uc = &uap->uc_mcontext.fpregs; int i, j, fop, frd, frs1, frs2; int *con = (int *) &zero_over_zero_value; /* * decode the trapping instruction to determine the * operation and source and destination registers */ fop = (uc->fpu_q->FQu.fpq.fpq_instr >> 5) & 0x1ff; frd = (uc->fpu_q->FQu.fpq.fpq_instr >> 25) & 0x1f; frs1 = (uc->fpu_q->FQu.fpq.fpq_instr >> 14) & 0x1f; frs2 = uc->fpu_q->FQu.fpq.fpq_instr & 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]; if (fop == 0x4e) /* instruction is fdivd */ if ((i | j) == 0) { /* both operands are 0 */ /* set rd to zero_over_zero_value */ uc->fpu_fr.fpu_regs[frd] = con[0]; uc->fpu_fr.fpu_regs[frd+1] = con[1]; } } } int main(void) { double x, w; int i, k; sigfpe_handler_type hdl, oldhdl; /* * save current invalid operation handler */ oldhdl = (sigfpe_handler_type) ieee_handler("get", "invalid", (sigfpe_handler_type)0); /* * set up new handler */ hdl = (sigfpe_handler_type) zero_over_zero_handler; (void) 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; (void) printf("Evaluating f(x) = (k*x)/sin(x)\n\n"); zero_over_zero_value = k; for (i = 5; i >= 0; i--) { x = (double) i * 0.1; w = (k * x) / sin(x); (void) printf("\tx=%3.3f\t f(x) = % 1.20e\n", x, w); } /* * restore old handler */ (void) ieee_handler("set", "invalid", oldhdl); return 0; }
此程序的输出如下所示:
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
请注意,如果 x = 0,则对 w 求值会导致尝试计算 0/0,从而发生无效操作异常。异常处理程序替代结果的值 2.0。
有关下列属性的说明,请参见 attributes(5):
|
sigaction(2)、signal(3c)、sigfpe(3C)、abort(3c)、fex_set_handling(3M)、ieee_flags(3M)、attributes(5)、siginfo.h(3HEAD)、signal.h(3HEAD)、ucontext.h(3HEAD)
可以使用 sigfpe(3c)、ieee_handler(3M) 或 fex_set_handling(3M) 中的任何一个安装浮点异常处理例程。不能在一个程序中混合使用这些方法。
在 Intel 系统上,只要启用了异常的陷阱(即“取消屏蔽”异常),就会进行 x87 浮点单元捕获,并引发与其对应的标志。因此,如果在调用 ieee_handler() 时已经引发了异常的标志,则通过 ieee_handler() 为异常启用陷阱将激发后续陷阱。要避免这种伪陷阱,程序应清除为其启用捕获的每个异常的对应标志,然后再调用 ieee_handler()。(ieee_flags(3M) 函数提供了一种清除异常标志的方式。)
通常,通过 ieee_handler() 安装的处理程序将直接输出某些诊断信息并终止程序。但是,如上面的示例所示,处理程序可能会正常返回,从而使控制权返回给执行捕获指令的线程。在当前的 SPARC 系统上,会从被捕获的指令后面的指令开始继续执行;在这种情况下,处理程序必须将结果提供给捕获指令的目标,使后续的计算可以使用该结果继续进行。在 Intel 系统上,如果捕获指令是 SSE 指令,则通过重新发出被捕获的指令来继续执行;在这种情况下,处理程序必须修改捕获指令的操作数,从而将计算重新发出时的可用结果;或者向捕获指令的目标提供一个可用结果,并显式前进到保存的指令指针,这样一来,便会从捕获指令之后的下一个指令开始恢复执行。如果捕获指令是 x87 指令,则从被捕获的指令之后的下一个浮点指令开始继续执行;在这种情况下,处理程序必须向捕获指令的目标提供一个可用结果,并在必要时调整保存的 x87 寄存器堆栈,从而使捕获指令表现为已完成。有关更多信息,请参见 Intel IA-32 体系结构手册。(请注意,fex_set_handling(3M) 提供了一个更简单的接口来处理浮点异常并继续执行。)