c++ comparison of two double values not working properly

56,568

Solution 1

Comparing floating point values using the == operator is very error prone; two values that should be equal may not be due to arithmetic rounding errors. The common way to compare these is to use an epsilon:

bool double_equals(double a, double b, double epsilon = 0.001)
{
    return std::abs(a - b) < epsilon;
}

Solution 2

There are two problems with floating point comparisons:

(1) Floating point operations usually involve at least tiny rounding errors which are hard to predict. Therefore two floating point operations that should mathematically give the same result (like 4.7 * (1.0 / 3.14) vs. 4.7 / 3.14) may give different results.

(2) The compiler is allowed to do floating point operations sometimes with higher precision than necessary. It is also allowed to do the exact same floating point operations with just the precision that was necessary at other times. Therefore the exact same operation may produce slightly different results, which is what you see here.

To solve the OP's problem, this looks like it is caused by (2). I'd try to find if there are any compiler options that can prevent the compiler from using higher precision than needed.

Share:
56,568

Related videos on Youtube

tuks
Author by

tuks

Updated on July 05, 2022

Comments

  • tuks
    tuks almost 2 years

    Look at this code:

    #include <cmath>
    #include <iostream>
    using namespace std;
    class Sphere
    {
        double r;
    public:
        double V() const { return (4/3) * 3.14 * pow(r,3); }
        bool equal(const Sphere& s) const
        {
            cout  << V() << " == " << s.V() << " : " << ( V() == s.V() );
            return ( V() == s.V() );
    
        }
    
        explicit Sphere(double rr = 1): r(rr){}
    
    };
    main()
    {
        Sphere s(3);
        s.equal(s);
    }
    

    The output is 84.78 == 84.78 : 0 which means the same method doesn't return the same value every time, even though all parameters are static?

    But if I write 3.0 instead of 3.14 in the V() method definition, like this:

    double V() const { return (4/3) * 3.0 * pow(r,3); }
    

    Then, the output is: 84.78 == 84.78 : 1

    What is going on here? I need this method, for my program, which will compare volumes of two objects, but it is impossible? I banged my head for so long to figure out what is the cause of the problem and luckily I found it, but now I don't understand why?? Does it have something to do with the compiler (GCC) or am I missing something important here?

    • yngccc
      yngccc over 10 years
      you simply don't test floating point number for equality like that.
    • Mike Seymour
      Mike Seymour over 10 years
      Usually it's a bad idea to test floating point values for equality, since small rounding errors can give unexpected results. But, as you say, this does the same calculation twice with the same input, so the test should pass. It does with at least one version of GCC: ideone.com/FPjRVN. What version and platform are you using?
    • Mike Seymour
      Mike Seymour over 10 years
      @tuks: Your professor might not have said anything, but another said rather a lot: cl.cam.ac.uk/teaching/1011/FPComp/floatingmath.pdf
    • tuks
      tuks over 10 years
      @AdamBurry yeah I see... it's because of (4/3), it should be (4.0/3)
  • Adam Burry
    Adam Burry over 10 years
    See for more detailed discussion: cygnus-software.com/papers/comparingfloats/…
  • Pete Becker
    Pete Becker over 10 years
    No. "Almost equals" is an advanced technique; it should not be used by beginners. One serious problem that it has it that a almost equals b and b almost equals c does not imply that a almost equals c.
  • nikolas
    nikolas over 10 years
    @PeteBecker Floating point arithmetics are not trivial, there is no "beginner's way" of doing this.
  • Pete Becker
    Pete Becker over 10 years
    @nijansen - true; but "almost equals" is almost always not the right solution.
  • gnasher729
    gnasher729 almost 10 years
    I wouldn't expect double_equals (1e-6, -1e-5) to return "true".

Related