clang和gcc之间的浮点操作结果不一致

clang和gcc之间的浮点操作结果不一致,c,gcc,floating-point,clang,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);

分别在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;
}
在ubuntu上,它会打印

FLT_EVAL_METHOD=0
temp2: 337500000.0000000000000000

有什么想法可以证明这一点以及如何使结果保持一致吗?

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

大多数实现的精度都在0.5 ULP以上,但这仍然足以使实际结果接近两个浮点之间的中点的难以舍入的结果不同。例如,如果实际结果是f2方向上的浮点f1的0.4 ULP,则一个实现可以返回f1和另一个返回f2,并且它们仍将精确到0.6 ULP,这非常好,但并不罕见

如果您希望在任何地方都获得相同的结果,您应该合并您自己的atanf实现,它仅由基本的IEEE 754操作组成。然后,它将在所有为基本操作(即大多数编译平台)提供IEEE 754语义的编译平台上生成相同的结果。这就是Java为使浮点基本函数的结果可复制所做的:它在上进行了标准化。如果你想在你想要的其他平台上编译它,你可以使用Stephen Canon指出的:就像OSX数学库中的许多其他函数一样,它提供了极好的标准遵从性,并且在准确性和速度之间进行了很好的权衡

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

编辑:


在传递给单精度反正切函数的大参数的特定情况下,一个实现可能会自动选择一个结果而不是它计算出的最精确结果的另一个原因:一个实现可能认为函数总是在-π/2和π/2之间返回结果是可取的。对于非常大的参数,实际结果几乎是π/2,与π/2最接近的单精度浮点近似值恰好在π/2以上。在这些情况下,atanf的一些实现选择返回紧靠π/2下方的浮点值,而其他实现可以选择返回紧靠π/2上方且最接近π/2的浮点值。我在一篇文章中讨论了这个问题,但对我的观点持保留态度:我不太使用浮点,所以我的观点不重要。这篇博文是在双精度的背景下写的,但实际上,在双精度中,我们幸运地遇到了函数atan的特殊情况:π/2的最接近的双近似值正好在它下面,因此实际上没有选择的必要。

32位IEEE754浮点值只有大约7位小数精度,所以这两个结果是一致的。这就是精度的含义。在苹果的atanf实现的评论中可以找到一个原因提示:其他人应该可以从那里得到它。@KerrekSB:如果精度就是这个意思,那么32位IEEE754浮点就不是这个意思。KerrekSB这句话反映了格式的精确性和操作的准确性之间的混淆。binary32 IEEE 754格式的精度相当于7位十进制数字,但应用于两个不同IEEE 754编译器平台上相同参数的两个基本运算的结果可以预期为2^-10000000000,因为它们可以预期相同。仅仅因为精度大约是7位小数,并不意味着两次计算的结果应该相差这么多。它们可以相差更多,也可以相差更少,因为它们是相同的。@PascalCuoq:是的,很好的观点,这种歧义确实来自于对库函数,甚至有时是对基本操作缺乏准确性约束;即使使用标准的数据类型表示法,C也不强制IEEE754操作语义。这通常是一个很好的答案,但AtanfGig的情况是一个有趣的特例,需要添加一些细节。数学上精确的结果是pi/2-tiny,它由两个单精度值L这两种说法都是正确的。但是,U>pi/2,因此返回U不满足结果在区间[-pi/2,pi/2]内的约束。这就是为什么我们选择在OSX数学库中返回L。@StephenCanon啊,是的,我没有注意到问题是关于这个的。我很久以前写过这篇文章,它的优点是指向了更多的文档片段:@supercat有太多的cospi,…实现,我不认为它不是IEEE 754的一部分。它们是计算f在sin,cos,tan中的fπx的函数。CRlibm有一组…pi三角函数。另一方面,我不记得在任何地方看到过任何过度的IARCTAN实现。@PascalCuoq让我们使用天真的错误统计:前24位是1.xx…x0 |下一个31位不能是| 100…0 | 1,否则我们将向下舍入两次而不是向上舍入一次我们的输入上有一个小于2^32的有限浮点,如果我们假设由下一个31位组成的整数的均匀分布,这并不是完全不吉利的。好的,如果函数f是反对称的f-u=+/-fu,有限输入少于2^31,但我们有另一个恼人的位模式1.xxx…1 | 0111…1 | 1。这是非常近似的,但可能值得扫描所有的浮点,我们在libm中有几个函数…@PascalCuoq:许多实现都有sin2pix等函数,尽管它们的精度不同;在有80位数学运算的机器上,生成一个好函数很容易,但在没有80位数学运算的机器上要困难得多。顺便说一句,我是唯一一个愿意看到一个受良好支持的浮点数据类型的人,它的行为类似于80位浮点加48位填充,而不是128位浮点类型?如果一个支持良好的类型比一个double多出16位,那么它所获得的边际收益将远远大于另外48位的类型。
FLT_EVAL_METHOD=0
temp2: 337500000.0000000000000000