-2

I found that some straightforward code results in an unexpected (and incorrect) inequality comparison on GCC 4.6.1 vs Visual Studio (2008 & 2012) or Clang 7.3.0 on OSX.

Here's a reduced snippet:

typedef float ftype;
struct Foo
{
    ftype v;

    Foo(): v(16.0f / 60.0f) {}
    ftype val() const {return v * 60.0f;}
};

void bar()
{
    Foo f;
    ftype v = f.val();
    if (v != f.val())
        printf("inequal");
}

If ftype is typedef'd as double then the problem doesn't occur. Could this be something to do with compiler inlining? Perhaps actually returning a double instead of a float?

NickD2039
  • 170
  • 1
  • 7
  • Related: http://stackoverflow.com/questions/29335982/floating-point-computation-changes-if-stored-in-intermediate-double-variable – M.M Sep 15 '16 at 06:10
  • I disagree that this is a dup of [Is floating point math broken?](http://stackoverflow.com/questions/588004/is-floating-point-math-broken) or [Floating point computation changes if stored in intermediate double variable](http://stackoverflow.com/questions/29335982/floating-point-computation-changes-if-stored-in-intermediate-double-variable). In this case, we are calling the exact same function twice, storing it in a variable of exactly the same type as the function return type, and it still doesn't compare equal *to itself*. – Raymond Chen Sep 15 '16 at 13:59
  • I concur that this is not a duplicate. The expressions and values involved are exactly the same, are only floats without type promotion, and should produce identical results. – doug Sep 15 '16 at 16:28
  • Thanks for the comments and feedback. I have further boiled this down to the point where f.val() != f.val() yields true, but only when -O2 is passed. This actually appears to be related to [link](http://stackoverflow.com/questions/7517588) and usage of the -ffloat-store gcc option has eliminated the problem. – NickD2039 Sep 20 '16 at 06:47

1 Answers1

1

In your snippet, you effectively compare two real numbers (floats or double) to be equal to each other using a simple "==" or "!=". Unfortunately, such approach does not work well with real numbers because of floating point operations taking place, i.e. real numbers are not stored exactly in the memory compared to integers.

You have to compare two real numbers taking some tolerance into account, where this tolerance depends on your used type (float or double) and machine.

While simple solutions, like the one below might work, you have to integrate it into your template-based code.

#include<limits>
#include<cmath>

TOL=std::numeric_limits<float>::epsilon()
if (std::abs(v - f.val())<TOL)
{
    //some code
}

you should take a look into the Floating-Point Guide. Those answers on StackOverflow should also help:

How to correctly and standardly compare floats?

What is the most effective way for float and double comparison?

UPD: bug using just the difference instead of abs value corrected per @doug's comment.

Community
  • 1
  • 1
Anton Menshov
  • 2,266
  • 14
  • 34
  • 55
  • 2
    You need to compare against the abs value of the difference. That only works for errors in one direction. – doug Sep 15 '16 at 06:25