下面的程序(改编自here)在使用GCC(4.8.2)和Clang(3.5.1)编译时给出了不一致的结果。特别是,即使FLT_EVAL_METHOD
发生变化,GCC结果也不会变化。
#include <stdio.h>
#include <float.h>
int r1;
double ten = 10.0;
int main(int c, char **v) {
printf("FLT_EVAL_METHOD = %d\n", FLT_EVAL_METHOD);
r1 = 0.1 == (1.0 / ten);
printf("0.1 = %a, 1.0/ten = %a\n", 0.1, 1.0 / ten);
printf("r1=%d\n", r1);
}
测试:
$ gcc -std=c99 t.c && ./a.out
FLT_EVAL_METHOD = 0
0.1 = 0x1.999999999999ap-4, 1.0/ten = 0x1.999999999999ap-4
r1=1
$ gcc -std=c99 -mfpmath=387 t.c && ./a.out
FLT_EVAL_METHOD = 2
0.1 = 0x0.0000000000001p-1022, 1.0/ten = 0x0p+0
r1=1
$ clang -std=c99 t.c && ./a.out
FLT_EVAL_METHOD = 0
0.1 = 0x1.999999999999ap-4, 1.0/ten = 0x1.999999999999ap-4
r1=1
$ clang -std=c99 -mfpmath=387 -mno-sse t.c && ./a.out
FLT_EVAL_METHOD = 2
0.1 = 0x0.07fff00000001p-1022, 1.0/ten = 0x0p+0
r1=0
注意,根据this blog post,GCC 4.4.3在第二个测试中输出0而不是1。
possibly related question表示GCC 4.6中的一个bug已经被纠正,这可能解释了为什么GCC的结果不同。
我想确认这些结果中是否有任何一个是不正确的,或者是否有一些微妙的评估步骤(例如,新的预处理器优化)将证明这些编译器之间的差异。
2条答案
按热度按时间zdwk9cvp1#
这个答案是关于你在进一步研究之前应该解决的问题,因为它会使推理发生的事情变得更加困难:
当然,打印
0.1 = 0x0.07fff00000001p-1022
或0.1 = 0x0.0000000000001p-1022
只能是在使用-mfpmath=387
时由于ABI不匹配而导致的编译平台上的错误。所有这些值都不能以过度精确为借口。您可以尝试在测试文件中包含您自己的转换为可读格式,以便该转换也可以使用
-mfpmath=387
进行编译。或者在另一个文件中创建一个小存根,不使用该选项编译,使用最小的调用约定:在其他文件中:
在用
-mfpmath=387
编译的文件中:l7mqbcuq2#
忽略Pascal Cuoq解决的
printf
问题,我认为GCC在这里是正确的:根据C99标准,FLT_EVAL_METHOD == 2
应将所有操作和常量的范围和精度计算为
long double
类型。因此,在这种情况下,
0.1
和1.0 / ten
都被计算为扩展精度近似值1/10。我不确定Clang在做什么,尽管this question可能会提供一些帮助。