为SIGFPE设置标志并继续执行忽略FPE

Fab*_*orr 3 c linux floating-point signals

在数值应用程序中,我想知道计算完成后是否发生浮点异常.默认情况下,将以静默方式忽略浮点除法和无效操作.

我的尝试是启用我关心的FPE,通过设置标志来处理SIGFPE并再次禁用它们以允许继续执行:

#include <fenv.h>
#include <signal.h>
#include <stdio.h>

int caught = 0;
struct sigaction old_sa;
/* (2) */ fenv_t fenv_hold;

void sighandler()
{
    caught = 1;
    printf("Caught in handler, disabling\n");
    /* (1) */ fedisableexcept(FE_ALL_EXCEPT);
    /* (2) */ feholdexcept(&fenv_hold);
    sigaction(SIGFPE, &old_sa, NULL);
}

int main(void)
{
    struct sigaction sa;
    volatile double a=1, b=0;

    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_SIGINFO; 
    sa.sa_sigaction = sighandler;
    sigaction(SIGFPE, &sa, &old_sa);

    feenableexcept(FE_DIVBYZERO);
    printf("Dividing by zero..\n");
    a/=b;
    printf("Continuing\n");
}
Run Code Online (Sandbox Code Playgroud)

我采取了两种方法,第一种标记(1),第二种标记(2).它们都没有按预期工作.

输出:

Dividing by zero..
Caught in handler, disabling
Floating point exception (core dumped)
Run Code Online (Sandbox Code Playgroud)

预期产出:

Dividing by zero..
Caught in handler, disabling
Continuing
Run Code Online (Sandbox Code Playgroud)

Eri*_*hil 6

如果您只是想知道,在计算完成后,是否发生了浮点异常,那么您不应该使用信号,因为它们的开销很高.而是使用浮点异常标志,这些标志在正常执行期间由处理器快速设置.

参见C标准<fenv.h>.简述:

  • 插入#include <fenv.h>源文件.
  • #pragma STDC FENV_ACCESS on在任何可能访问浮点标志的源代码之前插入或在非默认浮点模式下运行.
  • 如果需要,#pragma STDC FENV_ACCESS off在上面的源代码之后插入,当以下源代码不访问标志或在非默认模式下运行时.
  • 在计算之前,执行feclearexcept(FE_ALL_EXCEPT)清除标志.
  • 计算完成后,执行fetestexcept(exceptions)测试标志.exceptions应该是逐位的异或FE_DIVBYZERO,FE_INEXACT,FE_INVALID,FE_OVERFLOW,和/或FE_UNDERFLOW,以及可能的附加实现定义的标志.

请注意,某些C实现对访问浮点环境的支持较差.