首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >IEEE-754 C中的浮点异常

IEEE-754 C中的浮点异常
EN

Stack Overflow用户
提问于 2015-05-12 12:44:00
回答 3查看 579关注 0票数 5

我正在用C语言编写一个浮点计算器接口,它允许在运行时访问math.h中定义的数学函数。该接口是作为一个函数实现的,其行为类似于strtold()。它是基于ASCII的,应该和ASCII一样可移植,但为了实现这一点,我需要以尽可能可移植的方式处理浮点。我很高兴限制对IEEE-754浮点的支持,但我不确定如何处理IEEE-754定义的异常(溢出、下溢等)。首先,我非常确定检查在所有舍入模式下都有效的异常的唯一方法是检查状态标志本身;为此,我需要fenv.h (在C99的附录F中定义),所以我想知道fenv.h在实践中的可移植性。我也不完全理解fenv.h是如何工作的;在我看来,状态标志是集中的,但不知什么原因,我的印象是每个浮点都有内置的标志。此外,我知道C99说定义在math.h中的函数可能会溢出和下溢,但我不明白我应该如何检查这些异常。总而言之,我正在寻找如何使用fenv.h检查乘法导致的溢出的示例,以及如何正确检查math.h中定义的函数的说明。

EN

回答 3

Stack Overflow用户

发布于 2015-05-12 21:18:25

理论上,下面的函数将两个数字相乘,如果发生溢出则返回true,否则返回false

代码语言:javascript
复制
bool mul(double &a, double b) {
  feclearexcept(FE_OVERFLOW);
  a *= b;
  return fetestexcept(FE_OVERFLOW) != 0;
}

该标准规定,您需要使用#pragma FENV_ACCESS ON才能使用它。但是,我还没有使用过关心该编译指示的编译器,也没有使用过知道浮点乘法具有反映在异常标志中的副作用的编译器- gcc和clang都会很高兴地“优化掉”一个“死”的浮点操作。gcc bug 34678关注这一行为,我想在clang上也有类似的bug。此警告也适用于程序中舍入模式以外的任何舍入模式的使用。

票数 5
EN

Stack Overflow用户

发布于 2015-05-12 23:14:26

如果你正在构建一个计算器接口并且想要处理浮点异常,你应该:

任何浮点操作之前的

  • 重置标志操作

之后的

  • 测试标志

您将没有任何帮助,因此您必须手动实现if。示例:

代码语言:javascript
复制
double mul(double a, double b, int *status) {
    #pragma STDC FENV_ACCESS ON
    fexcept_t flags;
    int sv_status, f_status = -1;
    double resul;

    sv_status = fegetexceptflag(&flags, FE_ALL_EXCEPT) != 0; /* save flags */
    if (sv_status == 0) {
        f_status = feclearexcept(FE_ALL_EXCEPT);   /* clear all fp exception con
ditions */
    }
    resul = a * b;
    if (f_status == 0) {
        *status = fetestexcept(FE_ALL_EXCEPT); /* note conditions */
    }
    if (sv_status == 0) {
        fesetexceptflag(&flags, FE_ALL_EXCEPT); /* restore initial flags */
    }
    return resul;

}

演示:

代码语言:javascript
复制
int main ()
{
        double d2, d3, d4;
        int status;
        double d = 1e100;

        feraiseexcept(FE_OVERFLOW | FE_INEXACT);
        status = fetestexcept(FE_ALL_EXCEPT);
        printf("initial status : %x\n", status);
        d2 = mul(3., 4., &status);
        printf("resul 3 * 4 : %g - status %x (%x)\n", d2,
                status, fetestexcept(FE_ALL_EXCEPT));
        d2 = mul(d, d, &status);
        printf("resul d * d : %g - status %x (%x)\n", d2,
                status, fetestexcept(FE_ALL_EXCEPT));
        d2 = mul(d2, d, &status);
        printf("resul d *d *d : %g - status %x (%x)\n", d2,
                status, fetestexcept(FE_ALL_EXCEPT));
        d2 = mul(d2, d, &status);
        printf("resul d *d *d*d : %g - status %x (%x)\n", d2,
                status, fetestexcept(FE_ALL_EXCEPT));
        d2 = mul(d2, d, &status);
        printf("resul d *d *d*d*d : %g - status %x (%x)\n", d2,
                status, fetestexcept(FE_ALL_EXCEPT));
        return 0;
}

提供:

代码语言:javascript
复制
initial status : 28
resul 3 * 4 : 12 - status 0 (28)
resul d * d : 1e+200 - status 20 (28)
resul d *d *d : 1e+300 - status 20 (28)
resul d *d *d*d : inf - status 28 (28)
resul d *d *d*d*d : inf - status 0 (28)

这意味着mul

  • 正确设置状态标志
  • 保持浮点异常标志不变

因为这段代码只使用C规范中定义的宏和函数,所以它应该可以在任何兼容C99的编译器上运行。

现在是您实际处理标志的时候了。

参考资料:ISO/IEC 9899:201x (ISO C11) Committee Draft

注意: Clang (至少)发出一个警告,说它忽略了杂注STDS FENV_ACCESS,但它工作得很好。

票数 4
EN

Stack Overflow用户

发布于 2015-05-12 21:26:31

实际上,没有人会检查浮点异常。任何关心的人(这只是少数!)检查返回值:您可以轻松地检查结果是否为NaN、+/-无穷大或非正规化的数字。您需要手动检查以检测零是否是非零数相乘或相除的结果,但这非常容易。

没有人检查异常的一个原因是,我会发现让它在任何一个编译器上正确工作都是一件具有挑战性的事情,而且不可能让它在众多编译器上工作。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30181932

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档