http://www.perlmonks.org?node_id=1025415

WojciechGajewski has asked for the wisdom of the Perl Monks concerning the following question:

Dear all, I'm so confused. Just wrote this extremely basic program in Perl and it does not work in the way I expected it to work. Here is the code:
my $range = 0.1; my $i = 0; while( $i < 1 ) { print $i."\n"; $i = $i + $range; }
What does it return?
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1
Why the "1" at the end? I don't know. But that I can handle. Check out what happens if I want it to loop till $i is lower than 10. The code is now:
my $range = 0.1; my $i = 0; while( $i < 10 ) { print $i."\n"; $i = $i + $range; }
The result is:
0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2 2.1 2.2 2.3 2.4 2.5 2.6 2.7 2.8 2.9 3 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 4 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 5 5.1 5.2 5.3 5.4 5.5 5.6 5.7 5.8 5.9 5.99999999999999 6.09999999999999 6.19999999999999 6.29999999999999 6.39999999999999 6.49999999999999 6.59999999999999 6.69999999999999 6.79999999999999 6.89999999999999 6.99999999999999 7.09999999999999 7.19999999999999 7.29999999999999 7.39999999999999 7.49999999999999 7.59999999999999 7.69999999999999 7.79999999999999 7.89999999999999 7.99999999999999 8.09999999999999 8.19999999999999 8.29999999999999 8.39999999999999 8.49999999999999 8.59999999999999 8.69999999999999 8.79999999999998 8.89999999999998 8.99999999999998 9.09999999999998 9.19999999999998 9.29999999999998 9.39999999999998 9.49999999999998 9.59999999999998 9.69999999999998 9.79999999999998 9.89999999999998 9.99999999999998
This is crazy!! I had a course on numerical analysis and am aware that if you add very small numbers, some numerical errors can occur. But this? It's like Perl says 2+2 = 3.9999. What the hell? Please tell me what am I doing wrong. I am guessing it's something with types, confusing strings with numericals... All the very best, Wojciech.

Replies are listed 'Best First'.
Re: Simple adding numbers
by davido (Cardinal) on Mar 25, 2013 at 23:53 UTC

    This is the result of storing floating point numbers in base two. Consider the fraction 1/3rd. Expressed in decimal, that's 0.33 (repeating forever). Add 0.33 three times, and you get 0.99, not one. You can extend to as many decimal places as you like, you'll never get .3333333333333333333333333 * 3 to add up to 1.0. Well, in base 2, the fraction 1/10th has a non-terminating expansion. So 0.1 has to be stored as 0.09999999999999999 or something similar, and when you add that up ten times you won't get to one.

    It's only shocking because you grew up with base-ten and are so accustomed to seeing the problem that you don't notice it until it turns up in another base system.

    Of course this is not unique to Perl. Consider the following C++ code:

    #include <iostream> #include <iomanip> int main () { double n = 0.0; for( int i = 0; i != 10; i++ ) { n += 0.1; std::cout << std::setprecision(16) << std::fixed << n << std::endl +; } if( n != 1.0 ) std::cout << "Whoops, " << n << " isn't equal to 1.0." << std::end +l; return 0; }

    ...outputs...

    0.1000000000000000 0.2000000000000000 0.3000000000000000 0.4000000000000000 0.5000000000000000 0.6000000000000000 0.7000000000000000 0.7999999999999999 0.8999999999999999 0.9999999999999999 Whoops, 0.9999999999999999 isn't equal to 1.0.

    Dave

Re: Simple adding numbers
by toolic (Bishop) on Mar 25, 2013 at 23:54 UTC
Re: Simple adding numbers
by AnomalousMonk (Archbishop) on Mar 26, 2013 at 07:42 UTC
Re: Simple adding numbers
by ig (Vicar) on Mar 26, 2013 at 07:30 UTC

    This is just another way of saying the same thing but in Perl 0.1 isn't the value you think it is. Try the following:

    printf("%.64f\n", 0.1);

    On my system it prints:

    0.1000000000000000100000000000000000000000000000000000000000000000

    Note the extra 1 in the 17th place. The result may be different on your system. It depends on how floating point numbers are stored. Different systems store them differently.

Re: Simple adding numbers
by ww (Archbishop) on Mar 26, 2013 at 15:58 UTC

    "Why the "1" at the end? "

    Because the test in line 3 returns true until the last execution of line 6... at which time, $i is incremented to 1, and printed, BEFORE terminating the loop.

    If you didn't program your executable by toggling in binary, it wasn't really programming!

Re: Simple adding numbers
by nvivek (Vicar) on Mar 26, 2013 at 05:03 UTC

    When I used lt instead of < operator, it works as you expected. Still, lt operator used for comparing strings not for numerals. As per my understanding, perl tries to implicitly convert both $i and 1 as strings and do comparison. If somebody finds different reason for it, kindly clarify me.

    my $range = 0.1; my $i = 0; while( $i lt 1 ) { print $i."\n"; $i = $i + $range; }

      perl -E say 'Ouch!' if '0002' lt 1;

      Ouch!

      I would recommend caution if you're going to go down that road. The best thing to do is to simply understand the nature of floating point precision and deal with it in the most straight-forward way possible. Stringification has its uses, but bending the rules of numeric relational comparisons is probably not an ideal one.


      Dave

Re: Simple adding numbers
by hdb (Monsignor) on Mar 26, 2013 at 07:37 UTC

    This is one of the flaws in Perl. It usually makes your life so easy by providing simple means to achieve complex tasks that one often forgets that it is a mere programming language. (Hope this does not count as heresy.)

    A posting a day keeps Python away.