【问题标题】:SIGFPE ShenanigansSIGFPE 恶作剧
【发布时间】:2014-02-04 22:54:03
【问题描述】:

我重复相同的计算两次,但在一次我得到一个浮点异常,而在另一次我没有。

#include <iostream>
#include <cmath>
#include <fenv.h>

using namespace std;

int main(void)
{
  feenableexcept(-1);

  double x,y,z;

  x = 1.0;

  y = (1.0/(24.3*24.0*3600.0))*x;
  cout << "y = " << y << endl;

  z = x/(24.3*24.0*3600.0);
  cout << "z = " << z << endl;

  return 0;
}

我在 g++ 和 clang++ 上都对其进行了测试,并在两者中都得到了以下输出

y = 4.76299e-07
Floating point exception

发生了什么事?

【问题讨论】:

  • 什么平台/编译器? (这里可以,比如:ideone.com/SYmWam
  • 我的猜测是,在前面的计算中,除法是在编译时完成的,只剩下乘法。在后一种情况下,除法是在运行时完成的。编译器是否使用不同的算术?你在交叉编译吗?
  • 没有声明 feenableexcept(-1); 它工作正常。感觉与功能有关。我不知道它在做什么:(

标签: c++ sigfpe


【解决方案1】:

这是 FE_INEXACT 异常。
这意味着 x 乘以在编译时计算的常量 1/(24.3*24.0*3600.0) 不能转换为 double 而不会损失精度。

第一个操作不会引发此异常,因为 x 是 1.0,它具有精确表示,并且该常量在编译时已经转换为某些(不精确)双精度表示。

由于浮点异常处理没有标准化,这在其他编译器/平台上可能不会被注意到。

#include <iostream>
#include <cmath>
#include <fenv.h>

using namespace std;

int main(void)
{
  feenableexcept(FE_INEXACT); // comment this line out and the exception is gone

  double x,y,z;

  x = 1.0;

  y = (1.0/(24.3*24.0*3600.0))*x;
  cout << "y = " << y << endl;
  z = x/(24.3*24.0*3600.0);      // <-- FE_INEXACT exception
  cout << "z = " << z << endl;

  return 0;
}

默认情况下显然禁用此异常,否则您几乎无法进行任何浮点计算。

【讨论】:

    【解决方案2】:

    问题出在

    feenableexcept(-1);
    

    这组FPE exceptions 适用于所有可能的情况,包括不精确的结果(这在浮点运算中很常见)。你真的不应该在这里使用数字,而是使用提供的宏来设置你想要的位。

    替换为

    feenableexcept(FE_INVALID   | 
                   FE_DIVBYZERO | 
                   FE_OVERFLOW  | 
                   FE_UNDERFLOW);
    

    为我解决了这个问题。

    什么时候

    feenableexcept(FE_INVALID   | 
                   FE_DIVBYZERO | 
                   FE_OVERFLOW  | 
                   FE_UNDERFLOW |
                   FE_INEXACT);
    

    给出,SIGFPE 将返回。这表明 FE_INEXACT 是问题的根本原因。

    第一次计算没有给出 SIGFPE 的原因是在编译时已经完成了除法(结果不准确)。在运行时只执行乘法,这不会引入额外的不精确性。

    【讨论】:

    • 感谢 DL 先生的感谢。也可以随意复制示例代码,它没有版权:)。
    • @kuroineko 如果您查看原始问题下的 cmets,您会发现我只是包括我之前的评论。感谢您的信用 KN。
    猜你喜欢
    • 2011-07-31
    • 2010-09-23
    • 1970-01-01
    • 2011-10-09
    • 2015-01-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-30
    相关资源
    最近更新 更多