Oracle® Developer Studio 12.5:数值计算指南

退出打印视图

更新时间: 2016 年 6 月
 
 

4.3 检测异常

正如 IEEE 标准所要求的那样,基于 SPARC 和 x86 的系统上的浮点环境提供了用来记录所出现的浮点异常的状态标志。程序可通过测试这些标志来确定已发生了哪些异常。这些标志还可以被显式设置和清除。ieee_flags 函数提供一种访问这些标志的方法。在用 C 或 C++ 编写的程序中,C99 浮点环境函数提供另一种方法。

在基于 SPARC 的系统上,每种异常都有两个与之相关的标志:当前和已发生。 当前异常标志总是指出由上一个完成执行的浮点指令引发的异常。这些标志还累积(即“或”)到已发生异常标志中,从而记录自该程序开始执行或者自该程序上次清除已发生标志以来已经发生的所有未捕获异常。当浮点指令导致捕获的异常时,会设置与导致该陷阱的异常相对应的当前异常标志,但是不会更改已发生标志。当前异常标志和已发生异常标志都包含在浮点状态寄存器 %fsr 中。

在基于 x86 的系统上,浮点状态字 (SW) 为已发生异常以及浮点栈的状态提供标志。在基于 x86 的支持 SSE2 指令的系统上,MXCSR 寄存器包含记录由这些指令引发的已发生异常的标志。

4.3.1 ieee_flags(3m)

ieee_flags 为类似于 Oracle Developer Studio C、C++ 和 Fortran 的 IEE 754 异常标志提供了一个接口。然而,只能在 Oracle Solaris OS 上使用此接口。对移植范围更广的 C 和 C++ 程序使用C99 异常标志函数

调用 ieee_flags(3m) 的语法为:

i = ieee_flags(action, mode, in, out);

程序可提供字符串 "exception" 并将其作为第二个参数,从而可使用 ieee_flags 函数测试、设置或清除已发生异常状态标志。例如,要从 Fortran 中清除溢出异常标志,请编写:

      character*8 out
      call ieee_flags('clear', 'exception', 'overflow', out) 

要确定是否在 C 或 C++ 中发生了异常,请使用:

      i = ieee_flags("get", "exception", in, out); 

当操作为 "get" 时,out 中返回的字符串为以下之一:

  • "not available"-如果异常信息不可用

  • ""(空字符串)-如果没有已发生异常,或者对于 x86,非正规操作数是唯一的已发生异常

  • 在第三个参数 in 中命名的异常的名称,如果出现了该异常

  • 否则,返回已出现的、优先级最高的异常的名称

例如,在以下 Fortran 调用中,如果出现了除以零异常,则 out 中返回的字符串为 "division"。否则返回已出现的、优先级最高的异常的名称:

      character*8 out
      i = ieee_flags('get', 'exception', 'division', out)

请注意,除非 in 指定具体的异常,否则它将被忽略。例如,在 C 调用中,将忽略参数 "all"

      i = ieee_flags("get", "exception", "all", out); 

除了在 out 中返回异常的名称以外,ieee_flags 还返回一个结合当前引发的所有异常的整数值。此值是所有已发生异常标志的按位“或”,其中的每个标志都用一位表示,如表 32中所示。与每个异常相对应的位的位置由 fp_exception_type 值(定义在 sys/ieeefp.h 文件中)给出。(请注意,这些位的位置与计算机有关且不必连续。)

表 32  异常位
异常
位的位置
已发生异常位
invalid
fp_invalid
i & (1 << fp_invalid)
overflow
fp_overflow
i & (1 << fp_overflow)
division
fp_division
i & (1 << fp_division)
underflow
fp_underflow
i & (1 << fp_underflow)
inexact
fp_inexact
i & (1 << fp_inexact)
denormalized
fp_denormalized
i & (1 << fp_denormalized) (仅限 x86)

下面的 C 或 C++ 程序段显示一种用来对返回值进行解码的方法。

/*
 *   Decode integer that describes all accrued exceptions. 
 *   fp_inexact etc. are defined in <sys/ieeefp.h> 
 */ 
 
char *out; 
int invalid, division, overflow, underflow, inexact; 
 
code = ieee_flags("get", "exception", "", &out); 
printf ("out is %s, code is %d, in hex: 0x%08X\n", 
  out, code, code); 
inexact  =  (code >> fp_inexact) & 0x1; 
division  =  (code >> fp_division) & 0x1; 
underflow  =  (code >> fp_underflow) & 0x1; 
overflow  =  (code >> fp_overflow) & 0x1; 
invalid  =  (code >> fp_invalid) & 0x1; 
printf("%d %d %d %d %d \n", invalid, division, overflow,
  underflow, inexact); 

4.3.2 C99 异常标志函数

C/C++ 程序可以使用 C99 浮点环境函数测试、设置和清除浮点异常标志。头文件 fenv.h 定义五个与五个标准异常相对应的宏:FE_INEXACTFE_UNDERFLOWFE_OVERFLOWFE_DIVBYZEROFE_INVALID。它还定义要与所有这五个异常宏按位“或”的 FE_ALL_EXCEPT 宏。可将这些宏结合在一起,以便测试或清除异常标志的任何子集或者引发任何组合的异常。下面的示例显示如何将这些宏与几个 C99 浮点环境函数结合使用。有关更多信息,请参见 feclearexcept(3M) 手册页。


注 -  为了使行为保持一致,请不要在同一个程序中同时使用 libm 中的 C99 浮点环境函数和扩展以及 libsunmath 中的 ieee_flagsieee_handler 函数。

要清除这五个异常标志,请使用以下命令:

feclearexcept(FE_ALL_EXCEPT);

要测试引发的是无效运算标志还是除以零标志,请使用以下指令:

int i;

i = fetestexcept(FE_INVALID | FE_DIVBYZERO);
if (i & FE_INVALID)
    /* invalid flag was raised */
else if (i & FE_DIVBYZERO)
    /* division-by-zero flag was raised */

fegetexceptflagfesetexceptflag 函数提供一种用来保存和恢复标志子集的方法。下面的示例显示这些函数的一种使用方法。

fexcept_t flags;
 
/* save the underflow, overflow, and inexact flags */
fegetexceptflag(&flags, FE_UNDERFLOW | FE_OVERFLOW | FE_INEXACT);
/* clear these flags */
feclearexcept(FE_UNDERFLOW | FE_OVERFLOW | FE_INEXACT);
/* do a computation that can underflow or overflow */
...
/* check for underflow or overflow */
if (fetestexcept(FE_UNDERFLOW | FE_OVERFLOW) != 0) {
    ...
}
/* restore the underflow, overflow, and inexact flags */
fesetexceptflag(&flags, FE_UNDERFLOW | FE_OVERFLOW, | FE_INEXACT);