clang和gcc之间的浮点运算结果不一致

时间:2014-10-21 23:09:36

标签: c gcc floating-point clang

在OSX 10.10和ubuntu 14.04上单独编译并运行。

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

void testAtan() {
  float temp1 = 62981764.0000000000000000f;
  float temp2 = (2.14859168E8f  *  atanf(temp1));
  printf("temp2: %.16f\n", temp2);
}

int main() {
  printf("FLT_EVAL_METHOD=%d\n", FLT_EVAL_METHOD);
  testAtan();
  return 0;
}

在OS X上,它会打印

FLT_EVAL_METHOD=0
temp2: 337499968.0000000000000000

在ubuntu上打印

FLT_EVAL_METHOD=0
temp2: 337500000.0000000000000000

任何想法证明这一点以及如何使结果保持一致?

1 个答案:

答案 0 :(得分:8)

您正在调用一个库函数atanf,IEEE 754标准不要求它实现如此准确,以至于它会为所有实现产生相同的结果。

大多数实现具有高于0.5 ULP的分数的准确度,但是对于难以舍入的结果(实际结果接近两个浮点之间的中点的结果)而言,这仍然是足够的。例如,如果实际结果是浮点f1在f2方向上的0.4 ULP,则实现可以返回f1而另一个返回f2,它们仍然可以精确到0.6 ULP(这是非常好但不常见)。 / p>

如果您希望在任何地方获得相同的结果,您应该合并自己的atanf实现,仅由基本的IEEE 754操作组成。然后,它将在为基本操作(即大多数编译平台)提供IEEE 754语义的所有编译平台上产生相同的结果。这就是Java所做的使浮点基本函数结果可重现的原因:它在the “netlib” implementation上标准化。如果您设法在您希望定位的其他平台上编译它,您可以使用Stephen Canon指出的Apple implementation:与OS X数学库中的许多其他函数一样,它提供了出色的标准兼容性,并且非常准确性和速度之间的良好平衡。

您还必须使用任何“正确舍入”的数学库,然后结果将与任何其他正确舍入的数学库相同,因为对于任何基本函数的应用,只有一个正确舍入的结果任何争论。一个正确舍入的库是CRlibm,但重点是您可以使用任何其他库并获得与CRlibm相同的结果。 CRlibm只提供双精度函数,但是如果任何单精度标准函数的任何参数在正确舍入到单精度时产生不同的结果而不是正确舍入到双精度然后舍入到单精度,我会非常惊讶 - 精密。

修改

在传递给单精度反正切函数的大参数的特定情况下,还有一个原因可以解释为什么实现可以自愿选择除计算得到的最精确结果之外的结果:实现可能认为需要具有函数总是返回-π/ 2和π/ 2之间的结果。对于非常大的参数,实际结果接近π/ 2,并且最接近π/ 2的单精度浮点近似恰好高于π/ 2。在这些情况下,atanf的某些实现选择在紧接π/ 2之下返回浮点数,而其他实现可以选择返回上面(并且最接近)π/ 2的浮点数。我在一个blog post中讨论了这个问题(但是我的观点很简单:我不太使用浮点数,所以我的意见无关紧要)。博客文章是在双精度的背景下构建的,但实际上,在双精度的情况下,我们很幸运(对于函数atan的特定情况):最近的double逼近π/ 2恰好低于它,所以实际上没有必要做出选择。