8

在以下代码中,函数 foo1、foo2 和 foo3 旨在等效。但是,当 run foo3 没有从循环中终止时,是否有这种情况的原因?

template <typename T>
T foo1()
{
   T x = T(1);
   T y = T(0);
   for (;;)
   {
      if (x == y) break;
      y = x;
      ++x;
   }
   return x;
}

template <typename T>
T foo2()
{
   T x = T(0);
   for (;;)
   {
      T y = x + T(1);
      if (!(x != y)) break;
      ++x;
   }
   return x;
}

template <typename T>
T foo3()
{
   T x = T(0);
   while (x != (x + T(1))) ++x;
   return x;
}

int main()
{
   printf("1 float:  %20.5f\n", foo1<float>());
   printf("2 float:  %20.5f\n", foo2<float>());
   printf("3 float:  %20.5f\n", foo3<float>());
   return 0;
}

注意:这是使用 VS2010 在发布模式下使用 /fp 精确编译的。不确定 GCC 等将如何处理此代码,任何信息都会很棒。这可能是在 foo3 中,x 和 x+1 值以某种方式变为 NaN 的问题吗?

4

1 回答 1

13

发生的情况很可能如下。在 x86 架构上,可以使用 80 位精度完成中间计算(long double 是对应的 C/C++ 类型)。编译器将所有 80 位用于 (+1) 操作和 (!=) 操作,但在存储之前截断结果。

所以你的编译器真正做的是:

while ((long double)(x) != ((long double)(x) + (long double)(1))) {
  x = (float)((long double)(x) + (long double)(1));
} 

这绝对是不符合 IEEE 的,给每个人带来无尽的头痛,但这是 MSVC 的默认设置。使用/fp:strict编译器标志禁用此行为。

这是我大约 10 年前对这个问题的回忆,所以如果这不完全正确,请原谅我。有关 Microsoft 官方文档,请参见此处

编辑我很惊讶地得知 g++ 默认表现出完全相同的行为(在 i386 linux 上,但不是例如 -mfpmath=sse)。

于 2012-05-23T06:38:16.920 回答