相同 FLT_EVAL_METHOD,GCC/Clang 中的结果不同
Same FLT_EVAL_METHOD, different results in GCC/Clang
以下程序(改编自 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 -mpfmath=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。
A possibly related question 表示 GCC 4.6 中的错误已得到纠正,这可能解释了为什么 GCC 的结果不同。
我想确认这些结果中的任何一个是否不正确,或者一些微妙的评估步骤(例如新的预处理器优化)是否可以证明这些编译器之间的差异。
这个答案是关于你在继续之前应该解决的问题,因为它会让你更难推理否则会发生什么:
当然打印0.1 = 0x0.07fff00000001p-1022
或0.1 = 0x0.0000000000001p-1022
只能是使用-mfpmath=387
时ABI不匹配导致的编译平台错误。这些值中的 None 可以被过高的精度所原谅。
您可以尝试在测试文件中包含您自己的可读格式转换,以便该转换也可以使用 -mfpmath=387
进行编译。或者在另一个文件中制作一个小存根,而不是使用该选项编译,使用简约的调用约定:
在其他文件中:
double d;
void print_double(void)
{
printf("%a", d);
}
在用-mfpmath=387
编译的文件中:
extern double d;
d = 0.1;
print_double();
忽略 Pascal Cuoq 解决的 printf
问题,我认为 GCC 在这里是正确的:根据 C99 标准,FLT_EVAL_METHOD == 2
应该
evaluate all operations and constants to the range and precision of the long double
type.
因此,在这种情况下,0.1
和 1.0 / ten
都被评估为 1/10 的扩展精度近似值。
我不确定 Clang 在做什么,不过 this question 可能会提供一些帮助。
以下程序(改编自 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 -mpfmath=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。
A possibly related question 表示 GCC 4.6 中的错误已得到纠正,这可能解释了为什么 GCC 的结果不同。
我想确认这些结果中的任何一个是否不正确,或者一些微妙的评估步骤(例如新的预处理器优化)是否可以证明这些编译器之间的差异。
这个答案是关于你在继续之前应该解决的问题,因为它会让你更难推理否则会发生什么:
当然打印0.1 = 0x0.07fff00000001p-1022
或0.1 = 0x0.0000000000001p-1022
只能是使用-mfpmath=387
时ABI不匹配导致的编译平台错误。这些值中的 None 可以被过高的精度所原谅。
您可以尝试在测试文件中包含您自己的可读格式转换,以便该转换也可以使用 -mfpmath=387
进行编译。或者在另一个文件中制作一个小存根,而不是使用该选项编译,使用简约的调用约定:
在其他文件中:
double d;
void print_double(void)
{
printf("%a", d);
}
在用-mfpmath=387
编译的文件中:
extern double d;
d = 0.1;
print_double();
忽略 Pascal Cuoq 解决的 printf
问题,我认为 GCC 在这里是正确的:根据 C99 标准,FLT_EVAL_METHOD == 2
应该
evaluate all operations and constants to the range and precision of the
long double
type.
因此,在这种情况下,0.1
和 1.0 / ten
都被评估为 1/10 的扩展精度近似值。
我不确定 Clang 在做什么,不过 this question 可能会提供一些帮助。