http://qs321.pair.com?node_id=1212528

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

Hi, When I execute the following code I always get a value with something like 5.00999999999994. While the step size is 0.01. Can somebody explain this behavior?
#!/usr/bin/perl use warnings; my $size = 0.01; my $step_size = 0.01; while($size < 5){ $size = $size + $step_size; print $size."\n"; } print "FINAL: ".$size."\n";

Replies are listed 'Best First'.
Re: While loop with addition goes weird (binary float rounding error)
by LanX (Saint) on Apr 08, 2018 at 13:08 UTC
      Thanks for your reply. But to me, your answer is as difficult to understand as my problem ;) Can you explain it more simplistic? Maybe with example on how to do it? I'm just trying to add numbers here.

        Don't be so lazy, follow the two links posted by LanX++.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so". ;-)
        We can almost always ignore floating-point errors and round our results with printf. Unfortunately there is no "simplistic" way to recognize the rare cases where this is a bad idea (Or what to do about it if you really have one). The documents already cited are not easy to read! Be assured that they to not require knowledge of any math beyond elementary algabra - only a commitment to learn.
        Bill
Re: While loop with addition goes weird
by davido (Cardinal) on Apr 08, 2018 at 17:28 UTC

    I gave a more thorough explanation of this in Re: shocking imprecision. But the essential component is that we tend to think nothing of expressing 1/3rd as 0.33 in base 10, and casually understand out of habit that 0.33 + 0.33 + 0.33 is strictly 0.99, but implicitly 1 when dealing with decimal representations of fractions. However we are not as accustomed to dealing with binary fractional arithmetic.

    In base 10, any fraction that can be expressed as k/((2^n)*(5^m)) will have a terminating expansion, and any that cannot be expressed as such will not. In Base 2, the number must be able to be expressed as k/2^n to have a terminating expansion, and any that cannot be expressed as such will not terminate, meaning it cannot be expressed in a finite number of bits.

    Let's look at 0.01: 0.01 is approximately 1000/(2**16) or 1000/(2**17). Both are terrible approximations. Closer would be 10000/(2**20). But that's still a pretty poor approximation. And your calculation is relying on exactness, not approximations.


    Dave

Re: While loop with addition goes weird
by hippo (Bishop) on Apr 08, 2018 at 15:36 UTC
Re: While loop with addition goes weird
by Laurent_R (Canon) on Apr 08, 2018 at 14:30 UTC
    Computers are notoriously bad at computing with decimal numbers. That includes most programming languages.

    If you want accurate arithmetic, you might want to try using Perl 6:

    > my $size = 0.01; 0.01 > my $step_size = 0.01; 0.01 > $size += $step_size while $size < 5; Nil > say "FINAL: $size"; FINAL: 5
    ;-)
Re: While loop with addition goes weird
by jimpudar (Pilgrim) on Apr 08, 2018 at 16:57 UTC

    Hello chiel,

    You should definitely read the pages LanX linked to. The second especially is required reading for anyone who wants to write a computer program.

    However, I did want to point out you can use the bignum pragma to get Perl 5 to do what you want:

    $ perl -e ' use warnings; use bignum; my $size = 0.01; my $step_size = 0.01; while($size < 5){ $size = $size + $step_size; print $size."\n"; } print "FINAL: ".$size."\n";' ... 4.94 4.95 4.96 4.97 4.98 4.99 5 FINAL: 5

    Best,

    Jim

Re: While loop with addition goes weird
by poj (Abbot) on Apr 08, 2018 at 14:14 UTC

    This was answered a few weeks ago here

    poj
Re: While loop with addition goes weird
by Anonymous Monk on Apr 08, 2018 at 14:03 UTC
    As the articles that you've already been pointed to say, computers use a float-binary representation. The number 1/10 does not have an exact representation in base-2 floats, much as 1/3 does not have an exact representation in a base-10 float. Computers can handle this problem by using decimal (BCD = Binary-Coded Decimal) arithmetic, but Perl does not directly expose this type. One pragmatic solution to your problem might be to count using integers, where the integer is the desired number multiplied by 100. Then, each time, calculate the float by casting the integer to a float and dividing it by 100. Because this is done each time, errors do not accumulate. Many database systems employ a variation of this "scaled integer" strategy to handle their currency data type.