clang和gcc之间的浮点操作结果不一致
分别在OSX 10.10和ubuntu 14.04上编译和运行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);
#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,它由两个单精度值LFLT_EVAL_METHOD=0
temp2: 337500000.0000000000000000