Oracle Solaris Studio 12.4 Man Pages

Exit Print View

Updated: January 2015
 
 

ieee_handler(3M)

Name

ieee_handler - 管理浮点异常陷阱处理程序

Synopsis

cc [ flag ... ] file ...  -lsunmath -lm [ library ... ]
#include <sunmath.h>

long ieee_handler(const char *action, const char *exception,
     sigfpe_handler_type hdl);

Description

此函数同时为 ANSI/IEEE Std 754-1985 定义的五个浮点异常建立 SIGFPE 信号处理程序并控制浮点陷阱启用模式。actionexception 参数是指定要执行的操作的不区分大小写的字符串的指针。无效的参数字符串和无效参数组合会产生不确定的结果。

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() 将为指定的异常建立处理程序 hdlhdl 可以是 SIGFPE_DEFAULTSIGFPE_IGNORESIGFPE_ABORT 之一,或者是用户定义的 SIGFPE 处理程序的地址。(宏 SIGFPE_DEFAULTSIGFPE_IGNORESIGFPE_ABORT 以及类型 sigfpe_handler_type<floatingpoint.h> 中定义。)

如果 hdlSIGFPE_DEFAULTSIGFPE_IGNORE,则 ieee_handler() 将对指定的异常禁用捕获,从而后续发生的这些异常会导致 IEEE 754 指定的缺省行为。

如果 hdlSIGFPE_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 信号,程序必须:

  1. 使用 ieee_handler 设置处理程序。

  2. 执行生成预期意外的浮点操作。

以下程序说明了为无效操作异常设置处理程序并通过尝试计算 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

有关下列属性的说明,请参见 attributes(5):

属性类型
属性值
接口稳定性
Committed(已确定)
MT 级别
MT-Safe(MT 安全)

See also

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)

Notes

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