Thread: difference between two doubles is wrong

  1. #1
    Registered User
    Join Date
    May 2013
    Posts
    228

    difference between two doubles is wrong

    Hi guys, I'm new here.
    I'm trying to wrap my head around the precision issues when handling doubles.

    take a look at the following code:

    Code:
    int main()
    {
        double epsilon=0.001;
        double d1=2.334;
        double d2=2.335;
    
        cout<<"epsilon is: "<<epsilon<<endl;
        cout<<"d2-d1 is: "<<d2-d1<<endl;
    
        if ((d2-d1)==epsilon)
            cout<<"Equal!";
        else
            cout<<"Not equal!";
    }
    the output for this code is:

    Code:
    epsilon is: 0.001
    d2-d1 is: 0.001
    Not equal!
    what is going on here?


    thanks for the help!

  2. #2
    Registered User
    Join Date
    Aug 2005
    Location
    Austria
    Posts
    1,990
    Never try to check two floatingpoint variables for equality

    try this
    Code:
    #include <iostream>
    #include <iomanip>
    using namespace std;
    int main()
    {
        double epsilon=0.001;
        double d1=2.334;
        double d2=2.335;
     
        cout<<"epsilon is: "<< setprecision(20) << epsilon<<endl;
        cout<<"d2-d1   is: "<< setprecision(20) << d2-d1 <<endl;
     
        if ((d2-d1)==epsilon)
            cout<<"Equal!";
        else
            cout<<"Not equal!";
    }
    my output
    Code:
    epsilon is: 0.0010000000000000000208
    d2-d1   is: 0.00099999999999988986588
    Not equal!

    Kurt

  3. #3
    SAMARAS std10093's Avatar
    Join Date
    Jan 2011
    Location
    Nice, France
    Posts
    2,694
    Classic. You should use an approximation error, like this
    Code:
    If abs(x-y) < e 
    then equal
    Or as suggested here, use this
    Code:
    if(fabs(d2-d1) <= epsilon * fabs(d2))
    Last edited by std10093; 05-16-2013 at 02:44 PM.
    Code - functions and small libraries I use


    It’s 2014 and I still use printf() for debugging.


    "Programs must be written for people to read, and only incidentally for machines to execute. " —Harold Abelson

  4. #4
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    as std10093 eluded to, floating point numbers are only approximate. they are limited by their precision and may have rounding errors.
    What can this strange device be?
    When I touch it, it gives forth a sound
    It's got wires that vibrate and give music
    What can this thing be that I found?

  5. #5
    Registered User
    Join Date
    May 2013
    Posts
    228
    thanks you both for the help!

    Quote Originally Posted by ZuK View Post
    my output
    Code:
    epsilon is: 0.0010000000000000000208
    d2-d1   is: 0.00099999999999988986588
    Not equal!

    Kurt

    that is so weird.
    what went wrong here and why did setprecision() mess up the precision?

  6. #6
    Registered User
    Join Date
    Aug 2005
    Location
    Austria
    Posts
    1,990
    Quote Originally Posted by Absurd View Post
    what went wrong here and why did setprecision() mess up the precision?
    Nothing is messed up.
    The value 0.001 is not a power of two and can only be approximated.
    Kurt

  7. #7
    Registered User
    Join Date
    Oct 2006
    Posts
    3,445
    Quote Originally Posted by ZuK View Post
    Nothing is messed up.
    The value 0.001 is not a power of two and can only be approximated.
    Kurt
    powers of two and integer multiples thereof can be perfectly represented. everything else is an approximation.

    for example, 1.5 can be represented perfectly, because it is 3 * 2-1

    however, 3.6 cannot be exactly represented, because it is not an integer multiple of a power of 2.

    you can use this link to see what the actual binary representation of your floating point value will be.
    Last edited by Elkvis; 05-16-2013 at 03:34 PM. Reason: add link
    What can this strange device be?
    When I touch it, it gives forth a sound
    It's got wires that vibrate and give music
    What can this thing be that I found?

  8. #8
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    what went wrong here and why did setprecision() mess up the precision?
    O_o

    1): There is nothing wrong, as explained, because the values may be approximations; the result of floating-point operations always depends on how the approximations are resolved even when the target values themselves have been subject to the same approximations.

    2): The formatted streams do not print trailing fill by default; you need to use `std::fixed'.

    Soma

    Code:
    #include <iomanip>
    #include <iostream>
    
    const double kTarget(1.00048828125); // These values are subject to potentially greater errors
    const double kEpsilon(0.00048828125); // thanks to multiple operations when not accurately represented.
    
    double Update
    (
        const double fValue
      , const unsigned int fMultiple
    )
    {
        double sResult(fValue);
        for(unsigned int cMultiple(1); fMultiple > cMultiple; ++cMultiple)
        {
            sResult += fValue;
        }
        //sResult *= 1.25; // You may alternate between values which can be
        sResult *= 1.26;   // accurately represented and approximated values.
        return(sResult);
    }
    
    int main()
    {
        using namespace std;
        cout << fixed;
        for(unsigned int cMultiple(0); 10 > cMultiple; ++cMultiple)
        {
            double sValue1(Update(kTarget, cMultiple));
            double sDifference(Update(kEpsilon, cMultiple));
            double sValue2(sValue1 + sDifference);
            if((sValue2 - sValue1) != sDifference)
            {
                cout
                    << "Bad Result: "
                    << setprecision(16) << sValue1 << " : "
                    << setprecision(16) << sValue2 << " : "
                    << setprecision(16) << sDifference << '\n'
                ;
            }
        }
        return(0);
    }
    Last edited by phantomotap; 05-16-2013 at 05:01 PM.

  9. #9
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    Quote Originally Posted by Elkvis View Post
    however, 3.6 cannot be exactly represented, because it is not an integer multiple of a power of 2.
    This is not true.
    It can be exactly represented, but not using 32-bit floating point. There just aren't enough bits in the mantissa. But given enough bits in the mantissa, any number can be represented.
    Of course, machines only have limited storage available, which is why we have these truncation errors, as they are called.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  10. #10
    Registered User
    Join Date
    May 2013
    Posts
    228
    OK, I tried it this way, that still does not work:

    Code:
    #define EPSILON 0.001
    
    double abs(double number)
    {
        return number>0 ? number : -number;
    }
    
    int main()
    {
        double d1(4.000);
        double d2(4.001);
        cout<<(abs(d1-d2)<=EPSILON);
    }
    (I'm not using fabs() since we're not allowed to use any library other then iostream and sstream in our exercises)

    will produce the output:
    0

  11. #11
    C++まいる!Cをこわせ!
    Join Date
    Oct 2007
    Location
    Inside my computer
    Posts
    24,654
    abs(4.000 - 4.001) yields 0.0010000000000003340 > 0.001.
    Furthermore,
    #define EPSILON 0.001
    should be
    const double Epsilon = 0.001;
    Don't use #defines.
    Quote Originally Posted by Adak View Post
    io.h certainly IS included in some modern compilers. It is no longer part of the standard for C, but it is nevertheless, included in the very latest Pelles C versions.
    Quote Originally Posted by Salem View Post
    You mean it's included as a crutch to help ancient programmers limp along without them having to relearn too much.

    Outside of your DOS world, your header file is meaningless.

  12. #12
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    It can be exactly represented, but not using 32-bit floating point. There just aren't enough bits in the mantissa. But given enough bits in the mantissa, any number can be represented.
    O_o

    No. He is very definitely correct.

    There are infinitely many numbers you can't represent with floating-point values because of the way they work with powers of two regardless of how many bits you have available in the mantissa.

    Let's take an example: 0.49

    Here we have a number less than one which tells us a lot. The multiplier (significand) has an implied one as the most significant digit. (This is part of the IEEE 754 standard.) We know we need a negative exponent to reduce our value below the implied minimum value which is reasonably at least one (1.mantissa * (2 ^ exponent)). We can see also that a -1 exponent will not be sufficient because 0.5 is also greater than our target value. Our exponent is obviously going to be -2 so we will need a value four times our target from the multiplier.

    We now know need 0.96 in the mantissa (1.96 * (2 ^ -2)).

    This is too small: (1/2^1)+(1/2^2)+(1/2^3)+(1/2^4) = 0.9375
    This is too large: (1/2^1)+(1/2^2)+(1/2^3)+(1/2^4)+(1/2^5) = 0.96875

    We need something between those value: (1/2^1)+(1/2^2)+(1/2^3)+(1/2^4)+(1/2^6) = 0.953125

    Now we can begin to see where we will necessarily fail.

    As we can see from the table, the number of base 10 digits in the mantissa corresponds with the negative power of two; in other words, the greater the negative power of two, the more base 10 digits you need to represent the value.

    Code:
    1/2^1 = .5
    1/2^2 = .25
    1/2^3 = .125
    1/2^64 = 0.0000000000000000000542101086242752217003726400434970855712890625
    We can see that nothing will ever get us to the .96 value we need. The least significant "...5" value will always be out of our reach to "correct" by adding something to it because any smaller power of two will have its own "...5" least significant digit.

    The nearest we could possibly get, with a reduced exponent and a base 2 base, will always have a "...5" value in the least significant digit.

    Soma

  13. #13
    Registered User
    Join Date
    May 2003
    Posts
    1,619
    Quote Originally Posted by Elysia View Post
    This is not true.
    It can be exactly represented, but not using 32-bit floating point. There just aren't enough bits in the mantissa. But given enough bits in the mantissa, any number can be represented.
    Of course, machines only have limited storage available, which is why we have these truncation errors, as they are called.
    It would take an infinite number of bits to represent exactly; it's a repeating number in binary, the way that 1/6th is infinitely repeating in decimal.
    You ever try a pink golf ball, Wally? Why, the wind shear on a pink ball alone can take the head clean off a 90 pound midget at 300 yards.

  14. #14
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Cat
    It would take an infinite number of bits to represent exactly; it's a repeating number in binary, the way that 1/6th is infinitely repeating in decimal.
    I think it would still be impossible though since the cardinality of the set of real numbers is greater than the cardinality of the set of natural numbers (which applies to the number of bits you can have for a floating point representation).

    Disclaimer: mathematics? Is that edible?
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  15. #15
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    mathematics? Is that edible?
    ^_^

    Is the square-root or cake muffin?

    Soma

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. scanf() and doubles?
    By Frankie15 in forum C Programming
    Replies: 4
    Last Post: 09-27-2011, 05:53 PM
  2. Vanishing Doubles
    By EvilGuru in forum C Programming
    Replies: 1
    Last Post: 10-31-2005, 08:05 AM
  3. Using Doubles
    By jay kay in forum Windows Programming
    Replies: 4
    Last Post: 03-22-2005, 01:14 PM
  4. Problem with doubles
    By Asbestos in forum C++ Programming
    Replies: 14
    Last Post: 03-18-2005, 05:47 PM
  5. Please help - dividing doubles
    By hunterdude in forum C++ Programming
    Replies: 4
    Last Post: 08-05-2004, 12:06 AM