4

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

#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

这是怎么回事?

4

2 回答 2

5

那是 FE_INEXACT 异常。
这意味着x乘以1/(24.3*24.0*3600.0)编译时计算的常数不能在不损失精度的情况下转换为双精度。

第一个操作不会引发这个异常,因为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;
}

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

于 2014-02-04T14:43:12.957 回答
4

问题在于

feenableexcept(-1);

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

替换为

feenableexcept(FE_INVALID   | 
               FE_DIVBYZERO | 
               FE_OVERFLOW  | 
               FE_UNDERFLOW);

为我解决了这个问题。

什么时候

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

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

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

于 2014-02-04T14:42:09.377 回答