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) 提供了一个更简单的接口来处理浮点异常并继续执行。)