Martin Drozdik
Martin Drozdik

Reputation: 13323

Can two doubles be equal and not equal at the same time?

I have a very strange bug in my program. I was not able to isolate the error in a reproducible code but at a certain place in my code there is:

    double distance, criticalDistance;
    ...

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

In debug build everything is fine. Only one branch gets executed.

But in release build all hell breaks loose and sometimes both branches get executed.

This is very strange, since if I add the else conditional:

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

This does not happen.

Please, what can be the cause of this? I am using gcc 4.8.1 on Ubuntu 13.10 on a 32 bit computer.

EDIT1:

I am using precompiler flags

EDIT2:

I do not think this is caused by a memory leak. I analyzed both release and debug builds with valgrind memory analyzer with tracking of unitialized memory and detection of self-modifiyng code and I found no errors.

EDIT3:

Changing the declaration to

volatile double distance, criticalDistance;

makes the problem go away. Does this confirm woolstar's answer? Is this a compiler bug?

EDIT4:

using the gcc option -ffloat-store also fixes the problem. If I understand this correctly this is caused by gcc.

Upvotes: 4

Views: 1515

Answers (3)

woolstar
woolstar

Reputation: 5083

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

I have seen this behavior before in my own code. It is due to the mismatch between the standard 64 bit value stored in memory, and the 80 bit internal values that intel processors use for floating point calculation.

Basically, when truncated to 64 bits, your values are equal, but when tested at 80 bit values, one is slightly larger than the other. In DEBUG mode, the values are always stored to memory and then reloaded so they are always truncated. In optimized mode, the compiler reuses the value in the floating point register and it doesn't get truncated.

Upvotes: 14

Simon Karlsson
Simon Karlsson

Reputation: 4129

You are not initializing your doubles, are you sure that they always get a value?
I have found that uninitilized variables in debug is allways 0, but in release they can be pretty much anything.

Upvotes: 1

Stack Overflow is garbage
Stack Overflow is garbage

Reputation: 248209

Please, what can be the cause of this?

Undefined behavior, aka. bugs in your code.

There is no IEEE floating point value which exhibits this behavior. So what's happening is that you are doing something wrong, which violates an assumption made by your compiler.

When optimizing your code, the compiler assumes that your code can be described by the C++ standard. If you do anything that is left undefined by the C++ standard, then these assumptions are violated, resulting in "weird" execution. It could be something "simple" like an uninitialized variable or a buffer overrun resulting in parts of the stack or heap being overwritten with garbage data, or it could be something more subtle, where you rely on a specific ordering between two operations, which is not guaranteed by the standard.

That is probably why you were not able to reproduce the problem in a small test case (the smaller test code does not contain the erroneous code), or and why you only see the error in optimized builds.

Of course, it is also possible that you've stumbled across a compiler bug, but a bug in your code is quite a bit more likely. :)

And best of all, it means that we don't really have a chance to debug the problem from the code snippet you've shown. We can say "the code shouldn't behave like that", but that's about all.

Upvotes: 2

Related Questions