-2

I'm trying to compare 2 float variables.

But comparison failed because of different sign:

int main()
{
    //---------------------------------
    float d = 0.1;
    float f1, f2;
    printf("data: %.1f\n", d);
    //---------------------------------
    d -= 0.1; // 0.0
    f1 = d;
    printf("data: %.1f\n", d);
    printf("f1: %.1f\n", f1);
    //---------------------------------
    d -= 0.1; // -0.1
    printf("data: %.1f\n", d);
    //--------------------------------
    d += 0.1; // -0.0
    f2 = d;
    printf("data: %.1f\n", d);
    printf("f2: %.1f\n", f2);
    //---------------------------------

    if (f1 == f2)
    {
        printf("f1 and f2 equals\n");  // should get here
    }
    else
    {
        printf("f1 and f2 NOT equals\n");
    }

    if (f2 != -0.0)
    {
        printf("f2 NOT equal -0.0 \n"); // should get here
    }
    else
    {
        printf("f2 equals -0.0 \n"); 
    }
}

Output: output

  • data: 0.1
  • data: 0.0
  • f1: 0.0
  • data: -0.1
  • data: -0.0
  • f2: -0.0
  • f1 and f2 NOT equals
  • f2 NOT equal -0.0

How can I properly set conditions to compare -0.0 and 0.0 values?

Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94
M_V
  • 3
  • 3
  • 1
    Don't seem duplicate but related: [language agnostic - Is floating point math broken? - Stack Overflow](https://stackoverflow.com/questions/588004/is-floating-point-math-broken) – MikeCAT Mar 01 '19 at 12:12
  • 2
    Try adding a lot more decimal places to the outputs. – Weather Vane Mar 01 '19 at 12:14
  • 1
    Try changing all the floating point constants (which the compiler parses as doubles and then converts to floats) to genune float constants (eg replace 0.1 with 0.1f). – dmuir Mar 01 '19 at 12:19
  • Note that `0.1` cannot be exactly represented by binary floating point. Unlike in decimal, it is a recurring fraction. – Weather Vane Mar 01 '19 at 12:21
  • 1
    When doing floating point operations, due to the imperfect precision of using a finite number of binary digits to represent numbers that require more digits (finite or infinite), you cannot compare direct equality. You have to allow for some "tolerance" (*e.g.*, differing by less than some very small fraction). @Pedro provides a bit more detail in this regard. – lurker Mar 01 '19 at 12:29
  • Read http://floating-point-gui.de/ – Basile Starynkevitch Mar 01 '19 at 12:30
  • 1
    The question incorrectly asks about −0 and +0, but at least one of the actual numbers is not a zero. It is a small non-zero number that is merely shown with “0.0” due to limited output precision. Actual −0 and +0 compare as equal. – Eric Postpischil Mar 01 '19 at 12:39

2 Answers2

2

Its actually a limitation of accuracy that makes it difficult for you here, every calculation is made with limited accuracy

long explanation at Wikipedia

To overcome this you would need to allow certain ranges:

if (f1 == f2)

could become

if( ((f1 - f2) < 0.001) &&  ((f1 - f2) > -0.001) )

when you can tolerate the given range of 0.001, otherwise choose according to your need. Same for

if (f2 != -0.0)

you should specify a range here too, because with some calculations you only occasionally hit a given constant value. Hope this helps.

Pedro
  • 111
  • 4
  • Or a little more concisely, `if ( fabs(f1 - f2) < 0.001 )...` :) – lurker Mar 01 '19 at 12:30
  • thats better I guess, one might check which is actually faster if that matters, but I guess yours is best – Pedro Mar 01 '19 at 12:35
  • [Allowing “tolerance” in floating-point comparison is often not the right solution.](https://stackoverflow.com/a/49685306/298225) – Eric Postpischil Mar 01 '19 at 12:51
  • @Pedro I agree it may not be faster. It was just a mild suggestion since I think it's a little bit clearer. Mathematically, that's how one would normally express that inequality. – lurker Mar 01 '19 at 13:51
  • @Pedro: the fabs() version is more readable, i.e. it makes reading the code easier, and that is an important property of good source code. Speed is probably not much of an issue here. – Rudy Velthuis Mar 01 '19 at 14:13
  • @RudyVelthuis: No, not just as often. Certainly on Stack Overflow, the overwhelming majority of questions involving floating-point comparison are situations where a person incorrectly expected equality where it is not warranted, and using comparison with a tolerance will not solve the actual problem, at least not reliably. – Eric Postpischil Mar 01 '19 at 14:25
  • @Eric: IME, just as often. Your mileage may vary. – Rudy Velthuis Mar 01 '19 at 17:32
  • @RudyVelthuis: Would you provide some examples I could add to [the answer](https://stackoverflow.com/a/49685306/298225)? – Eric Postpischil Mar 02 '19 at 01:04
0

In C, 0.1 is a double precision floating point constant i.e. it can be thought of as having type double. 0.1 cannot be represented exactly by either a float or a double. It's a bit like 1/3 is not representable exactly in base 10 with a finite number of digits.

The conversions needed to add 0.1 to a float lose precision (it chops off some of the fraction digits) which makes it hard to do equality comparisons.

You have three options. You're program will work as you expect if you either

  • replace the float declarations with double declarations
  • replace every instance of 0.1 with 0.1f
  • use Pedro's suggestion

The former makes your variables double precision numbers and the later makes your constants single precision numbers.

There is, however, a wider issue with comparing floating point values for equality in that, except for fractions where the denominator is a power of 2, no number is expressible exactly as a float or a double. You need to be doing something like what Pedro suggests.

JeremyP
  • 84,577
  • 15
  • 123
  • 161