两个双打是否可以相等而不是同时相等?

时间:2014-01-09 15:39:56

标签: c++ floating-point

我的程序中有一个非常奇怪的错误。我无法在可重现的代码中隔离错误,但在我的代码中的某个位置有:

    double distance, criticalDistance;
    ...

    if (distance > criticalDistance)
    {
        std::cout << "first branch" << std::endl;
    }
    if (distance == criticalDistance)
    {
        std::cout << "second branch" << std::endl;
    }

在调试版本中,一切都很好。只有一个分支被执行。

但是在发布版本中,所有地狱都会破裂,有时两个分支都会被执行。

这很奇怪,因为如果我添加其他条件

    if (distance > criticalDistance)
    {
        std::cout << "first branch" << std::endl;
    }
    else if (distance == criticalDistance)
    {
        std::cout << "second branch" << std::endl;
    }

这不会发生。

请问,这可能是什么原因?我在 32位计算机上的 Ubuntu 13.10 上使用 gcc 4.8.1

EDIT1:

我正在使用预编译器标志

  • -std = GNU ++ 11
  • -gdwarf-3

EDIT2:

我不认为这是由内存泄漏引起的。我用 valgrind 内存分析器分析了发布和调试版本,跟踪了单元化内存并检测到自修改代码,我发现没有错误。

EDIT3:

将声明更改为

volatile double distance, criticalDistance;

让问题消失。这是否确认woolstar's answer?这是编译器错误吗?

EDIT4:

使用gcc选项 -ffloat-store 也可以解决问题。如果我理解正确,这是由gcc引起的。

3 个答案:

答案 0 :(得分:14)

if (distance > criticalDistance)
  // true
if (distance == criticalDistance)
  // also true

我之前在自己的代码中看到过这种行为。这是由于存储在内存中的标准64位值与英特尔处理器用于浮点计算的80位内部值不匹配。

基本上,当截断为64位时,您的值相等,但在以80位值测试时,一个稍大于另一个。在DEBUG模式下,值始终存储在内存中,然后重新加载,因此它们始终被截断。在优化模式下,编译器重用浮点寄存器中的值,它不会被截断。

答案 1 :(得分:2)

  

请问,原因可能是什么?

未定义的行为,又名。代码中的错误。

没有IEEE浮点值表现出这种行为。所以发生的事情是你正在做某些错误,这违反了编译器的假设。

在优化代码时,编译器假定您的代码可以用C ++标准来描述。如果您执行C ++标准未定义的任何操作,则会违反这些假设,从而导致执行“怪异”。它可能是一个“简单”的东西,如未初始化的变量或缓冲区溢出导致堆栈或堆的部分被垃圾数据覆盖,或者它可能是更微妙的东西,你依赖于两个操作之间的特定顺序,这是不符合标准。

这可能就是为什么你无法在一个小的测试用例中重现问题(较小的测试代码不包含错误的代码),或者为什么你只看到优化版本中的错误。

当然,您也可能偶然发现了编译器错误,但代码中的错误更有可能发生。 :)

最重要的是,这意味着我们没有机会从您显示的代码段中调试问题。我们可以说“代码不应该那样”,但这就是全部。

答案 2 :(得分:1)

你没有初始化你的双打,你确定他们总是得到一个价值吗? 我发现调试中未经过激励的变量总是0,但在发布时它们几乎可以是任何东西。