【问题标题】:Same FLT_EVAL_METHOD, different results in GCC/Clang相同的 FLT_EVAL_METHOD,GCC/Clang 中的不同结果
【发布时间】:2015-03-04 09:55:14
【问题描述】:

以下程序(改编自 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。

possibly related question 表示在 GCC 4.6 中已更正了一个错误,这可能解释了为什么 GCC 的结果不同。

我想确认这些结果中是否有任何不正确,或者是否有一些微妙的评估步骤(例如新的预处理器优化)可以证明这些编译器之间的差异是合理的。

【问题讨论】:

  • 我最近不得不调查旧 GCC 版本的行为,有人指点我 gcc.godbolt.org,这非常有用。它没有 GCC 4.4.3,但它有 4.4.7。
  • gcc -std=c99 -mpfmath=387 t.c &amp;&amp; ./a.out 真的是gcc -std=c99 -mfpmath=387 t.c &amp;&amp; ./a.out(错字?)

标签: c gcc floating-point clang


【解决方案1】:

这个答案是关于你应该在进一步解决之前解决的问题,因为否则它会使推理发生的事情变得更加困难:

肯定打印0.1 = 0x0.07fff00000001p-10220.1 = 0x0.0000000000001p-1022只能是使用-mfpmath=387时ABI不匹配导致的编译平台上的错误。这些值中的任何一个都不能被过度精确所原谅。

您可以尝试在测试文件中包含您自己的转换为可读格式,以便该转换也可以使用-mfpmath=387 编译。或者在另一个文件中创建一个小存根,而不是使用该选项编译,使用简约的调用约定:

在其他文件中:

double d;
void print_double(void)
{
  printf("%a", d);
}

在用-mfpmath=387编译的文件中:

extern double d;
d = 0.1;
print_double();

【讨论】:

  • 确实,首先编译没有-mfpmath=387print_double 文件,然后将其.o 链接到另一个文件解决了这个问题:它在两个编译器中打印0x1.999999999999ap-4,有和没有-mfpmath=387 标志。
  • @CuriosGuy 请注意,您使用的样式是通过内联反引号` 获得的,适用于代码。 OTOH 没有关于程序输出的建议,所以这里可能可以接受(但不要使用` 强调)。
  • 先生,在我的程序中,它说找不到 FLT_EVAL_METHOD ,这是为什么呢?
  • @CuriosGuy FLT_EVAL_METHOD 必须由符合 C99 的编译器的标头 float.h 定义。也许你的 C 编译器不支持 C99?
【解决方案2】:

忽略 Pascal Cuoq 解决的 printf 问题,我认为 GCC 在这里是正确的:根据 C99 标准,FLT_EVAL_METHOD == 2 应该

根据long double 类型的范围和精度评估所有操作和常量。

因此,在这种情况下,0.11.0 / ten 都被评估为 1/10 的扩展精度近似值。

我不确定 Clang 在做什么,尽管 this question 可能会提供一些帮助。

【讨论】:

    猜你喜欢
    • 2021-09-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-25
    • 2016-01-08
    • 1970-01-01
    相关资源
    最近更新 更多