原始双重类型比较的GCC问题

8 c++ double gcc compare

我有以下一些代码,但是当使用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;
}
Run Code Online (Sandbox Code Playgroud)
  • 编译时使用:"g ++ -pedantic -Wall -Werror -O1 -o test test.cpp"我得到以下输出:"error @:3"

  • 编译时使用:"g ++ -pedantic -Wall -Werror -O2 -o test test.cpp"我得到以下输出:"error @:3"

  • 编译时使用:"g ++ -pedantic -Wall -Werror -O3 -o test test.cpp"我没有错误

  • 编译时使用:"g ++ -pedantic -Wall -Werror -o test test.cpp"我没有错误

我不认为这是与舍入有关的问题,或者比较中的epsilon差异.我已经尝试过使用英特尔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;在"返回1"之前添加,我在运行时的任何优化模式都没有错误.

这是我的代码中的错误,或者GCC是否有问题以及它处理双重类型的方式?

注意:我刚刚尝试使用gcc版本4.3和3.3,但没有显示错误.

解决方案: Mike Dinsdale注意到以下错误报告:http : //gcc.gnu.org/bugzilla/show_bug.cgi?id = 323 看来GCC团队并不完全确定问题的性质.

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

Mik*_*ale 7

结果取决于优化设置这一事实表明它可能是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);
}
Run Code Online (Sandbox Code Playgroud)

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

  • 到32位?你检查过差异了吗?它确实那么大吗?如果是这样,那看起来像是gcc中的一个bug.如果不是,我会下注这是因为将FPU寄存器保存到内存引起的80位 - > 64位截断.你有valgrind安装?如果是这样,我将通过valgrind检查问题是否消失. (3认同)
  • @Mike:这是一个很棒的发现!我尝试使用ffloat-store选项,因为它们有效,但是我的一些单元测试现在运行在他们平常的90%左右.看起来我将在Linux上使用英特尔,直到clang完全进入城镇. (2认同)
  • 乐意效劳!(我花了两天的时间来弄清楚我第一次遇到这种情况时发生的事情;))你是否尝试过关闭扩展精度,例如通过调用我的(名字很长的名字!)set_extended_precision_is_on(false) - 我认为那是什么VS等人.隐式地做,所以它可能会避免你的速度命中. (2认同)

Mic*_*urr 4

该问题可能是由于在存储表达式结果时丢失了一些精度,而编译器没有在本地存储表达式结果作为优化:

double d = v[i % 4] * i;  // the result, `d`, might be kept in a register 
                          //   instead of storing it in a memory location, 
                          //   keeping full precision

if(lst[i] != d) {         // the value stored in lst[i] may have lost some 
                          //   precision since it had to be stored in memory,
                          //   which might not be able to hold the full 
                          //   precision that the expression generated
Run Code Online (Sandbox Code Playgroud)

C99标准在6.3.1.8/2“常用算术转换”中说:

浮点操作数的值和浮点表达式的结果可以用比类型所要求的更高的精度和范围来表示;类型不会因此改变。

  • @Monomer:这种行为的效果很可能受到实现、平台和优化设置的强烈影响。这类事情是我很高兴我很少需要处理浮点的原因之一。 (3认同)