Comparing double in for loop

Discussion in 'C++' started by eman.abu.samra@gmail.com, Apr 9, 2008.

  1. Guest

    Hi all,

    i have encountered the strangest behavior. Check out this simple
    program:

    #include <stdio.h>

    int main()
    {
    double time = 1;
    double i = 0;

    for (double i = 0; i < time ; i+=0.01 )
    {
    if ( i == 0.15 )
    {
    printf( "found %f\n", i);
    }
    if ( i < 0.1 )
    {
    printf( "foundXX %f\n", i);
    }
    }

    return 1;
    }

    What would you expect to be printed:
    All the numbers from 0.0 to 0.9 with the prefex: foundXX
    and last line in output should be found 0.15 - right?!
    Wrong... what I get is all the numbers from 0.0 to 0.1 printed
    (including 0.1!!)
    When checking if ( i==0.1) { printf( "foundXX %f\n",i);} it does not
    print foundXX 0.1!!
    Why exactly does it think that 0.1 is < than 0.1!!??

    anyone?

    Thanks
     
    , Apr 9, 2008
    #1
    1. Advertising

  2. Tim Love Guest

    Tim Love, Apr 9, 2008
    #2
    1. Advertising

  3. Guest

    On 9 Apr., 15:26, wrote:
    > i have encountered the strangest behavior. Check out this simple
    > program:

    [..]
    > What would you expect to be printed:
    > All the numbers from 0.0 to 0.9 with the prefex: foundXX
    > and last line in output should be found 0.15 - right?!
    > Wrong... what I get is all the numbers from 0.0 to 0.1 printed
    > (including 0.1!!)


    The number 0.1 is not representable in the internal format for double
    numbers of your compiler, see for example IEEE-754. In binary format,
    0.1 has a infinite length periodic mantissa:
    0.1 (10) == 0.00011001100110011... (2), with a period length of four
    digits. This is analog to 1/3 with base 10: 1/3 = 0,33333... (10) and
    with base 3 it is just 0.1 (3), no periodic repetition.

    The compiler selects the closest match it can store internally which
    is off by ~10^(-18) for Visual C++ 2005. Your repeated adding of this
    only-nearly-0.1 number accumulates an error that gives you the
    "strange" behavior. Machine double numbers are a finite subset of the
    infinite real number domain. The operations on them executed by a
    computer are similar but not identical to the math +, -, *, /, ...
    operators 0.1 + 0.1 + 0.1 + 0.1 + 0.1 .

    You should not check identity of two doubles like in "if ( i ==
    0.15 ) ...". Better define a error margin, eg:

    const double eps = 1E-9;
    if ( i < 0.15 + eps && i > 0.15 - eps ) ...

    This will save you from some nasty surprises in real applications.
    Note, you should adapt eps according to your expected accumulated
    error, depending on the operations you executed before, 10^(-9) may be
    to small or too large.

    > When checking if ( i==0.1) { printf( "foundXX %f\n",i);} it does not
    > print foundXX 0.1!!
    > Why exactly does it think that 0.1 is < than 0.1!!??


    Try a variation on the 2nd printf() in your code:

    printf( "foundXX %20.18lf (%20.18lf)\n", i, i - 0.1 );

    > Thanks


    My pleasure.

    Michael.
     
    , Apr 9, 2008
    #3
  4. Matthias Buelow, Apr 9, 2008
    #4
  5. wrote:
    > for (double i = 0; i < time ; i+=0.01 )
    > {
    > if ( i == 0.15 )
    > {
    > printf( "found %f\n", i);
    > }
    > if ( i < 0.1 )
    > {
    > printf( "foundXX %f\n", i);
    > }
    > }


    As others have pointed out, 0.01 cannot be represented accurately with
    floating point numbers (for the same reason as eg. 1/3 cannot be
    represented accurately with decimal numbers).

    Clearly you want a precise number of iterations to your loop, and you
    want precise control on what happens with some precise values of the
    loop counter. In those cases what you should do is to use an integer
    loop counter, and calculate the floating point value from it. In other
    words:

    for(int i = 0; i < 100; ++i)
    {
    double value = i/100.0;

    if(i == 15) std::cout << "found " << value << "\n";
    if(i < 10) std::cout << "foundXX " << value << "\n";
    }

    The integer loop counter will make sure that an accurate number of
    iterations is performed, and comparing this integer loop counter in the
    conditionals will make sure that those conditionals give an accurate
    result. Whenever the floating-point value needs to be used, use the
    'value' variable, as exemplified above.
     
    Juha Nieminen, Apr 10, 2008
    #5
  6. wrote:
    > Machine double numbers are a finite subset of the
    > infinite real number domain. The operations on them executed by a
    > computer are similar but not identical to the math +, -, *, /, ...
    > operators 0.1 + 0.1 + 0.1 + 0.1 + 0.1 .



    Then why doesn't an implementation use a large number of bits, for
    people that want accuracy and also for x==y to work?


    Like 8,000 bits or 8,000,000 bits or any number of bits necessary to
    store all the possible numbers for double for example.
     
    Ioannis Vranos, Apr 10, 2008
    #6
  7. Jim Langston Guest

    "Ioannis Vranos" <> wrote in message
    news:ftjkmb$2dbu$...
    > wrote:
    >> Machine double numbers are a finite subset of the
    >> infinite real number domain. The operations on them executed by a
    >> computer are similar but not identical to the math +, -, *, /, ...
    >> operators 0.1 + 0.1 + 0.1 + 0.1 + 0.1 .

    >
    >
    > Then why doesn't an implementation use a large number of bits, for
    > people that want accuracy and also for x==y to work?
    >
    >
    > Like 8,000 bits or 8,000,000 bits or any number of bits necessary to
    > store all the possible numbers for double for example.


    Since 0.1 in binary is an infinate regression, it would take an infinite
    number of bits to represent it.

    --
    Jim Langston
     
    Jim Langston, Apr 10, 2008
    #7
  8. Ioannis Vranos wrote:
    > Then why doesn't an implementation use a large number of bits, for
    > people that want accuracy and also for x==y to work?


    Sure, if you want your program to be a thousand times slower.

    The C++ compiler will usually use the FPU (and sometimes even SSE) to
    make floating point calculations in hardware. To get larger floating
    point values you would have to use a software library, which would be
    enormously slower.

    Besides, 0.1 is inaccurate in binary floating point format regardless
    of the number of bits used (for the exact same reason as 1/3 is
    inaccurate in decimal format regardless of how many decimals you use).

    > Like 8,000 bits or 8,000,000 bits or any number of bits necessary to
    > store all the possible numbers for double for example.


    double already stores all possible numbers representable with a double.
     
    Juha Nieminen, Apr 10, 2008
    #8
  9. Guest

    On 10 Apr., 01:49, Ioannis Vranos <>
    wrote:
    > wrote:
    > > Machine double numbers are a finite subset of the
    > > infinite real number domain. The operations on them executed by a
    > > computer are similar but not identical to the math +, -, *, /, ...
    > > operators 0.1 + 0.1 + 0.1 + 0.1 + 0.1 .


    oops. " != 0.5 " is missing.

    >
    > Then why doesn't an implementation use a large number of bits, for
    > people that want accuracy and also for x==y to work?
    >
    > Like 8,000 bits or 8,000,000 bits or any number of bits necessary to
    > store all the possible numbers for double for example.


    A large number of bits is not enough, you'd need an *inifinte* number
    of bits. Even if you extend the "normal" double numbers concept by
    fractions (e.g. store two integer numbers 1 and 10 to represent 1/10),
    you cannot represent the whole rational set (e.g. sqrt(2), pi or e
    cannot be stored like this without loss of precision). Also, there is
    an issue in performance: the more memory you use to store a single
    value, the longer it will take to operate on them.

    AFAIR, the current implementation by x86 CPUs uses 80 binary digits
    for an extended precision floating point number internally and 64
    binary digits to store a double in RAM memory. Should be enough for
    most applications, *if* you follow the caveats mentioned before. -
    Especially do not compare floats/doubles for (in)equality by != / ==
    operators. If you want more precision you need to do it by application
    code. This is considerably slower than using direct CPU/FPU commands
    for floating point.

    best,
    Michael.
     
    , Apr 10, 2008
    #9
  10. James Kanze Guest

    On Apr 10, 1:49 am, Ioannis Vranos <>
    wrote:
    > wrote:
    > > Machine double numbers are a finite subset of the
    > > infinite real number domain. The operations on them executed by a
    > > computer are similar but not identical to the math +, -, *, /, ...
    > > operators 0.1 + 0.1 + 0.1 + 0.1 + 0.1 .


    > Then why doesn't an implementation use a large number of bits, for
    > people that want accuracy and also for x==y to work?


    > Like 8,000 bits or 8,000,000 bits or any number of bits
    > necessary to store all the possible numbers for double for
    > example.


    I'm not sure I understand. A double has enough bits to store
    all possible numbers for double. By definition---if the number
    can't be stored in a double, then it's not a possible number for
    double.

    The problem here is that the real number 0.1 is not a possible
    number for double. And if you're asking that the implementation
    use enough bits to store all possible real numbers, that's
    lg2(N), where N is the number of possible real numbers. And off
    hand, how many possible real numbers would you say there are?

    In this particular case, an implementation could have masked the
    problem by using a decimal representation for double. But that
    will fail as soon as you try something like:

    step = 1.0/3.0 ;
    for ( value = 0.0 ; value != 1.0 ; value += step ) ...

    (Back when I was working in this environment, I actually wrote a
    proof that for all finite representations of floating point
    values, the loop:

    step = 1.0/N ;
    for ( value = 0.0 ; value != 1.0 ; value += step )

    will fail for some values of N.)

    --
    James Kanze (GABI Software) email:
    Conseils en informatique orientée objet/
    Beratung in objektorientierter Datenverarbeitung
    9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
     
    James Kanze, Apr 10, 2008
    #10
  11. On 9 Apr, 14:26, wrote:


    > i have encountered the strangest behavior. Check out this simple
    > program:
    >
    > #include <stdio.h>
    >
    > int main()
    > {
    >   double time = 1;
    >   double i = 0;
    >
    >   for (double i = 0; i < time ; i+=0.01 )
    >   {
    >     if ( i == 0.15  )
    >     {
    >       printf( "found %f\n", i);
    >     }
    >     if ( i < 0.1 )


    here you test if i < 0.1. Assuming the first
    test fails (it is very likely to) this is
    the only print statement in your program


    >     {
    >       printf( "foundXX %f\n", i);
    >     }
    >   }
    >
    >   return 1;
    >
    > }
    >
    > What would you expect to be printed:
    > All the numbers from 0.0 to 0.9 with the prefex: foundXX


    um. Why would you expect anything larger than 0.1 to be
    printed?


    > and last line in output should be found 0.15 - right?!


    no

    > Wrong... what I get is all the numbers from 0.0 to 0.1 printed
    > (including 0.1!!)


    kool. So what is the problem :)

    > When checking if ( i==0.1) { printf( "foundXX %f\n",i);} it does not
    > print foundXX 0.1!!
    > Why exactly does it think that 0.1 is < than 0.1!!??


    so other responses


    --
    Nick Keighley
     
    Nick Keighley, Apr 10, 2008
    #11
  12. Juha Nieminen wrote:
    > Ioannis Vranos wrote:
    >> Then why doesn't an implementation use a large number of bits, for
    >> people that want accuracy and also for x==y to work?

    >
    > Sure, if you want your program to be a thousand times slower.
    >
    > The C++ compiler will usually use the FPU (and sometimes even SSE) to
    > make floating point calculations in hardware. To get larger floating
    > point values you would have to use a software library, which would be
    > enormously slower.
    >
    > Besides, 0.1 is inaccurate in binary floating point format regardless
    > of the number of bits used (for the exact same reason as 1/3 is
    > inaccurate in decimal format regardless of how many decimals you use).



    OK, but in math we have a symbol to represent the infinite repeating
    sequences in decimals, like 7.33333... or 7.343434...
     
    Ioannis Vranos, Apr 10, 2008
    #12
  13. Ioannis Vranos wrote:
    > OK, but in math we have a symbol to represent the infinite repeating
    > sequences in decimals, like 7.33333... or 7.343434...


    Floating point numbers are not the set of real numbers, nor are they
    symbolic numbers. They are binary numbers with a fixed amount of bits.
    You can't expect to be able to do with them what you can do in
    "mathematics". You can only expect being able to approximate a finite
    amount of operations.
     
    Juha Nieminen, Apr 10, 2008
    #13
  14. brian tyler Guest

    > A large number of bits is not enough, you'd need an *inifinte* number
    > of bits. Even if you extend the "normal" double numbers concept by
    > fractions (e.g. store two integer numbers 1 and 10 to represent 1/10),
    > you cannot represent the whole rational set (e.g. sqrt(2), pi or e
    > cannot be stored like this without loss of precision). Also, there is
    > an issue in performance: the more memory you use to store a single
    > value, the longer it will take to operate on them.


    /pedant mode on

    sqrt(2), pi and e are not in "the rational set" they are irrational,
    meaning that they cannot be written as the quotient of two integers.
    Moreover pi and e are transcendental meaning that they cannot be
    expressed as the root of a polynomial equation (like sqrt(2) can since
    it is one root of X^2 - 2 = 0). What you mean is the real set.

    /pedant mode off
     
    brian tyler, Apr 10, 2008
    #14
  15. Juha Nieminen wrote:
    > Ioannis Vranos wrote:
    >> OK, but in math we have a symbol to represent the infinite repeating
    >> sequences in decimals, like 7.33333... or 7.343434...

    >
    > Floating point numbers are not the set of real numbers, nor are they
    > symbolic numbers. They are binary numbers with a fixed amount of bits.
    > You can't expect to be able to do with them what you can do in
    > "mathematics". You can only expect being able to approximate a finite
    > amount of operations.



    Well I could think of an implementation where the (last) repeating
    sequences could be identified with the use of extra bits. For example an
    additional byte could be reserved and used for this in the style:

    10000000 = The last decimal digit is repeating indefinetely,
    example: 1.1111111111111111....

    11000000 = The two last decimal digits are repeating indefinitely,
    example: 1.1212121212121212....

    11100000 = The three last decimal digits are repeating indefinitely,
    example: 2.233234123123123123....

    11110000 = The four last decimal digits are repeating indefinitely,
    example, 1.543424321432143214321.....

    etc.
     
    Ioannis Vranos, Apr 10, 2008
    #15
  16. Ioannis Vranos wrote:
    > Juha Nieminen wrote:
    >> Ioannis Vranos wrote:
    >>> OK, but in math we have a symbol to represent the infinite repeating
    >>> sequences in decimals, like 7.33333... or 7.343434...

    >> Floating point numbers are not the set of real numbers, nor are they
    >> symbolic numbers. They are binary numbers with a fixed amount of bits.
    >> You can't expect to be able to do with them what you can do in
    >> "mathematics". You can only expect being able to approximate a finite
    >> amount of operations.

    >
    >
    > Well I could think of an implementation where the (last) repeating
    > sequences could be identified with the use of extra bits. For example an
    > additional byte could be reserved and used for this in the style:
    >
    > 10000000 = The last decimal digit is repeating indefinetely,
    > example: 1.1111111111111111....
    >
    > 11000000 = The two last decimal digits are repeating indefinitely,
    > example: 1.1212121212121212....
    >
    > 11100000 = The three last decimal digits are repeating indefinitely,
    > example: 2.233234123123123123....
    >
    > 11110000 = The four last decimal digits are repeating indefinitely,
    > example, 1.543424321432143214321.....
    >
    > etc.



    More accurately an example of storing the value of the second example
    would be:

    Stored in double bits:

    1.12


    Repeating flags (in our case one byte reserved for this):

    11000000 = means .12 is repeated indefinitely
     
    Ioannis Vranos, Apr 10, 2008
    #16
  17. Ioannis Vranos wrote:
    > Juha Nieminen wrote:
    > Well I could think of an implementation where the (last) repeating
    > sequences could be identified with the use of extra bits. For example an
    > additional byte could be reserved and used for this in the style:


    How about simply using integers, as I suggested in my other post?
    Much easier and very efficient.
     
    Juha Nieminen, Apr 10, 2008
    #17
  18. Guest

    On 10 Apr., 15:48, brian tyler <> wrote:
    > /pedant mode on
    > What you mean is the real set.
    > /pedant mode off


    I was only testing if you all pay attention. Honest. No, really. Could
    these eyes lie???

    :) You got me here.

    Michael
     
    , Apr 10, 2008
    #18
  19. Guest

    On 10 Apr., 16:01, Ioannis Vranos <>
    wrote:
    > Juha Nieminen wrote:
    > Well I could think of an implementation where the (last) repeating
    > sequences could be identified with the use of extra bits. For example an
    > additional byte could be reserved and used for this in the style:


    By adding some extra bits, you certainly can extend the set of
    representable real numbers. However, the length of the periodically
    repeated portion also can have arbitrary length (up to the size of
    the denominator if you convert to a normalized fraction). A number
    like 1/x where x is roughly 10^9 may have a worst case period length
    of 10^9... (of course, not all numbers in this range are *so* nasty).
    You'd need to store this sequence somewhere at least once.

    You'd be better off by just adding the extra bits to the mantissa.

    best,

    Michael
     
    , Apr 10, 2008
    #19
  20. Brian Tyler Guest

    On Thu, 10 Apr 2008 07:40:06 -0700, Michael.Boehnisch wrote:

    > On 10 Apr., 15:48, brian tyler <> wrote:
    >> /pedant mode on
    >> What you mean is the real set.
    >> /pedant mode off

    >
    > I was only testing if you all pay attention. Honest. No, really. Could
    > these eyes lie???
    >
    > :) You got me here.
    >
    > Michael


    Haven't used the pedant mode tags in a while ;)

    Brian
     
    Brian Tyler, Apr 11, 2008
    #20
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. =?iso-8859-1?Q?Juli=E1n?= Albo

    Problem comparing a double with 0

    =?iso-8859-1?Q?Juli=E1n?= Albo, Jul 2, 2003, in forum: C++
    Replies:
    2
    Views:
    361
    =?iso-8859-1?Q?Juli=E1n?= Albo
    Jul 2, 2003
  2. Sydex
    Replies:
    12
    Views:
    6,568
    Victor Bazarov
    Feb 17, 2005
  3. John
    Replies:
    11
    Views:
    2,191
  4. Replies:
    6
    Views:
    305
  5. Isaac Won
    Replies:
    9
    Views:
    405
    Ulrich Eckhardt
    Mar 4, 2013
Loading...

Share This Page