使用带C的编译器选项捕获浮点异常

时间:2013-09-05 19:14:02

标签: c floating-point fortran scientific-computing

Gfortran 有一个方便的-ffpe-trap编译器选项,但 gcc 没有类似的选项。我隐约意识到他们以不同的方式处理异常,但还不足以知道为什么只能通过启用编译器标志来死于 FPE ,而另一个则需要包含额外的代码才能打开异常。

1 个答案:

答案 0 :(得分:3)

对不起文字之墙;真正的答案在底部。

浮点异常由C99中的库代码控制,而不是由编译器标志控制。这是一个例子:

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

#define PRINTEXC(ex, val) printf(#ex ": %s\n", (val & ex) ? "set" : "unset");

double foo(double a, double b) { return sin(a) / b; }

int main()
{
    int e;
    double x;

    feclearexcept(FE_ALL_EXCEPT);

    x = foo(1.2, 3.1);

    e = fetestexcept(FE_ALL_EXCEPT);
    PRINTEXC(FE_DIVBYZERO, e);
    PRINTEXC(FE_INEXACT, e);
    PRINTEXC(FE_INVALID, e);
    PRINTEXC(FE_OVERFLOW, e);
    PRINTEXC(FE_UNDERFLOW, e);

    putchar('\n');

    feclearexcept(FE_ALL_EXCEPT);

    x += foo(1.2, 0.0);

    e = fetestexcept(FE_ALL_EXCEPT);
    PRINTEXC(FE_DIVBYZERO, e);
    PRINTEXC(FE_INEXACT, e);
    PRINTEXC(FE_INVALID, e);
    PRINTEXC(FE_OVERFLOW, e);
    PRINTEXC(FE_UNDERFLOW, e);
    return lrint(x);
}

输出:

FE_DIVBYZERO: unset
FE_INEXACT: set
FE_INVALID: unset
FE_OVERFLOW: unset
FE_UNDERFLOW: unset

FE_DIVBYZERO: set
FE_INEXACT: set
FE_INVALID: unset
FE_OVERFLOW: unset
FE_UNDERFLOW: unset

更新:使用GNU GCC,您可能会导致浮点异常陷阱并发送信号:

#pragma STDC FENV_ACCESS on

#define _GNU_SOURCE
#include <fenv.h>

int main()
{
#ifdef FE_NOMASK_ENV
    fesetenv(FE_NOMASK_ENV);
#endif

    // ...
}

但是,当您收到SIGFPE时,您应该做什么并不完全清楚,因为您无法撤消故障指令。 (并参阅@ EricPostpischil关于pragma的评论;谢谢!)