双乘法在32位平台的编译时和运行时之间不同

时间:2015-07-14 22:05:24

标签: c double 32bit-64bit

我正在32位和64位平台上编译并运行以下程序:

int main()
{
  double y = 8.34214e08;
  double z = 1.25823e45;

  return y * z == 8.34214e08 * 1.25823e45;
}

虽然在64位中结果是预期的(值是相同的,退出代码是非零的)在32位似乎在编译时计算的值之间有一点差别,比较的右边,并且左侧在运行时计算。

这是编译器中的错误还是有逻辑解释?

编辑:这与Why comparing double and float leads to unexpected result?不同,因为这里的所有值都是双倍的。

2 个答案:

答案 0 :(得分:19)

IEEE-754允许以更高的精度完成中间计算(强调我的)。

  

(IEEE-754:2008)"语言标准还应定义并要求实现提供允许和禁止对块进行单独或共同的值更改优化的属性。这些优化可能包括但不限于:[...] 在表达式评估中使用更广泛的中间结果。"

例如在IA-32上,双值可以更精确地存储在x87 FPU寄存器中(80位而不是64位)。因此,您实际上是将双精度乘法与双精度乘法相乘进行比较。

例如,在结果为1的x64上(不使用x87 FPU作为SSE),添加gcc选项-mfpmath=387以使用x87会产生结果在我的机器上更改为0

如果你想知道C是否也允许这样做,那就是:

  

(C99,6.3.1.p8)"浮动操作数和浮动表达式结果的值可以比该类型所需的精度和范围更精确地表示;"

答案 1 :(得分:5)

在编译时完成的浮点计算通常比double在运行时使用的精度更高。此外,C可以以更高的double精度执行运行时中间long double计算。要么解释你的不平等。有关详细信息,请参阅FLT_EVAL_METHOD

  volatile double y = 8.34214e08;
  volatile double z = 1.25823e45;
  volatile double yz = 8.34214e08 * 1.25823e45;
  printf("%.20e\n", y);
  printf("%.20e\n", z);
  printf("%.20e\n", yz);
  printf("%.20Le\n", (long double) y*z);
  printf("%.20Le\n", (long double) 8.34214e08 * 1.25823e45);

8.34214000000000000000e+08
1.25822999999999992531e+45
// 3 different products!
1.04963308121999993395e+54
1.04963308121999993769e+54
1.04963308122000000000e+54

您的搜索结果可能略有差异。