Search code examples
c++floating-pointcomparison

Comparing floats in c++


I'm learning c++ from a tutorial and there, I was told that comparing floats in c++ can be very comfusing. For example, in the below code:

#include <iostream>

using namespace std;

int main()
{
    float decimalNumber = 1.2;
    
    if (decimalNumber == 1.2){
        cout << "Equal" << endl;
    }else{
        cout << "Not equal" << endl;
    }

    return 0;
}

I would get "not equal". I agree on this point. The tutor told that If we need to compare floats, we can use > to the nearest number. (that would not be very precise). I searched for different ways to compare floats in google and I got many complex ways of doing that.

Then I created a program myself:

#include <iostream>

using namespace std;

int main()
{
    float decimalNumber = 1.2;
    
    if (decimalNumber == (float)1.2){
        cout << "Equal" << endl;
    }else{
        cout << "Not equal" << endl;
    }

    return 0;
}

After type-casting like above, I got "Equal".

The thing I want to know is that should I use the above way to compare floats in all of my programs? Does this have some cons?

Note : I know how a number is represented exactly in the memory and how 0.1 + 0.2 !- 0.3 as described in another SO Question. I just want to know that can I check the equality of two floats in the above way?


Solution

  • The reason that second example works is, in a way, pure chance.

    Some factors to consider:

    1. When both sides of the comparison are of type float, the compiler is probably more likely to "optimise out" the comparison so that it just happens during the compilation process. It can look at the literals and realise that the two numbers are logically the same, even though at runtime they may differ at lower levels of precision.

    2. When both sides of the comparison are of type float, they have the same precision, so if you've created the values in the same way (here, by a literal), any error in the lower levels of precision could be identical. When one of them is a double, you have additional erroring bits at the end that throw off the comparison. And if you'd created one via 0.6 + 0.6 then the result could also be different as the errors propagate differently.

    In general, do not rely on this. You can't really predict how "accurate" your floats will be when they contain numbers not representable exactly in binary. You should stick to epsilon-range compares (where appropriate) if you need loose value comparison, even if direct comparison appears to "work" occasionally without it.

    A good approach to take, if you don't actually need floating point, is to use fixed point instead. There are no built-in fixed-point types in the language, but that's okay because you can simulate them trivially with a little arithmetic. It's hard to know what your use case is here, but if you only need one decimal place, instead you can store int decimalNumber = 12 (i.e. shift the decimal point by one) and just divide by ten whenever you need to display it. Two ints of value 12 always compare nicely.

    Money is a great example of this: count in pennies (or tenths of pennies), not in pounds, to avoid errors creeping in that scam your customers out of their cash. 😉