原始双类型比较的 GCC 问题

2023-11-21

我有以下代码,但是当使用带有各种优化标志的 GCC 4.4 编译它时,我在运行时得到了一些意想不到的结果。

#include <iostream>

int main()
{
   const unsigned int cnt = 10;
   double lst[cnt] = { 0.0 };
   const double v[4] = { 131.313, 737.373, 979.797, 731.137 };

   for(unsigned int i = 0; i < cnt; ++i) {
      lst[i] = v[i % 4] * i;
   }

   for(unsigned int i = 0; i < cnt; ++i) {
      double d = v[i % 4] * i;
      if(lst[i] != d) {
         std::cout << "error @ : " << i << std::endl;
         return 1;
      }
   }
   return 0;
}
  • 当编译时:“g++ -pedantic -Wall -Werror -O1 -o test test.cpp”我得到以下输出:“错误@:3”

  • 当编译时:“g++ -pedantic -Wall -Werror -O2 -o test test.cpp”我得到以下输出:“错误@:3”

  • 当编译时:“g++ -pedantic -Wall -Werror -O3 -o test test.cpp”我没有收到任何错误

  • 当编译时:“g++ -pedantic -Wall -Werror -o test test.cpp”我没有收到任何错误

我不认为这是与舍入或比较中的 epsilon 差异相关的问题。我已经在 Intel v10 和 MSVC 9.0 上尝试过此操作,它们似乎都按预期工作。我相信这应该只不过是按位比较。

如果我将 if 语句替换为以下内容:if (static_cast<long long int>(lst[i]) != static_cast<long long int>(d)),并添加“-Wno-long-long”,运行时我在任何优化模式下都没有收到错误。

如果我添加std::cout << d << std::endl;在“return 1”之前,运行时我在任何优化模式下都没有收到错误。

这是我的代码中的错误,还是 GCC 及其处理双精度类型的方式有问题?

Note:我刚刚用 gcc 版本 4.3 和 3.3 尝试过此操作,没有显示该错误。

解决:Mike Dinsdale 指出了以下错误报告:http://gcc.gnu.org/bugzilla/show_bug.cgi?id=323看来GCC团队不是完全确定关于问题的性质。

正如错误报告中所建议的,可能的解决方案是使用 ffloat-store 选项。我已经尝试过这个并且它有效,但是从性能角度来看结果并不是那么好,尽管 ymmv。


结果取决于优化设置这一事实表明,这可能是 x87 扩展精度造成的混乱(正如 Michael Burr 所说)。

下面是我使用的一些代码(在 x86 处理器上使用 gcc)来关闭扩展精度:

static const unsigned int PRECISION_BIT_MASK = 0x300;
///< bitmask to mask out all non-precision bits in the fpu control word \cite{INTEL}.
static const unsigned int EXTENDED_PRECISION_BITS = 0x300;
///< add to the fpu control word (after zeroing precision bits) to turn on extended precision \cite{INTEL}.
static const unsigned int STANDARD_PRECISION_BITS = 0x200;
///< add to the fpu control word (after zeroing precision bits) to turn off extended precision \cite{INTEL}.

void set_fpu_control_word(unsigned int mode)
{
  asm ("fldcw %0" : : "m" (*&mode));
}

unsigned int get_fpu_control_word()
{
  volatile unsigned int mode = 0;
  asm ("fstcw %0" : "=m" (*&mode));
  return mode;
}

bool fpu_set_extended_precision_is_on(bool state)
{
  unsigned int old_cw = get_fpu_control_word();
  unsigned int masked = old_cw & ~PRECISION_BIT_MASK;
  unsigned int new_cw;
  if(state)
    new_cw = masked + EXTENDED_PRECISION_BITS;
  else
    new_cw = masked + STANDARD_PRECISION_BITS;
  set_fpu_control_word(new_cw);
  return true;
}

bool fpu_get_extended_precision_is_on()
{
  unsigned int old_cw = get_fpu_control_word();
  return  ((old_cw & PRECISION_BIT_MASK) == 0x300);
}

或者您可以使用 valgrind 运行您的代码,它不会模拟 80 位寄存器,并且对于像这样的短程序来说可能更容易!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

原始双类型比较的 GCC 问题 的相关文章

随机推荐