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

Hi all,

I was debugging a script with which I was having a calculation issue, and traced the problem to some code that does some simple arithmetic. I boiled down the problem to a short test script:
#!/usr/bin/perl use strict; my @a; $a[0] = (0.1 - 0.1) * 10; #Should be 0 $a[1] = (0.2 - 0.1) * 10; #Should be 1 $a[2] = (0.3 - 0.1) * 10; #Should be 2 $a[3] = (0.4 - 0.1) * 10; #Should be 3 $a[4] = (0.5 - 0.1) * 10; #Should be 4 my $i; for( $i = 0; $i < 5; $i++ ) { print "$i: ", $a[$i], " ", int($a[$i]), "\n"; } print "\n"; for( $i = 0; $i < 5; $i++ ) { printf "%d: %s %d %f\n", $i, $a[$i], $a[$i], $a[$i]; }
And here is the output:
0: 0 0 1: 1 1 2: 2 1 * 3: 3 3 4: 4 4 0: 0 0 0.000000 1: 1 1 1.000000 2: 2 1 2.000000 * 3: 3 3 3.000000 4: 4 4 4.000000
On the lines with *s you would expect it to print '2 2' & '2 2 2.000000', but instead it prints '1's. I tested both 'print' & 'printf' here to see if it was an issue with one of those functions alone, or perhaps the data type the $a[]'s were assumed to be. This only seems to happen for the calculation of
$a[2] = (0.3 - 0.1) * 10;
(perhaps there are other instances). So it seems like $a[2] is not equal to int($a[2]), though they should be the same.

Does anyone know why this happens?

Please forgive if this is a known phenomenon. I'm not sure how to do a search for this type of problem.

BTW, I tested in Windows XP (Perl 5.8.7), OS X 10.4 (Perl 5.8.6), AIX (Perl 5.6.1), & Ubuntu (Perl 5.10.0) -- all the same result.

TIA

Replies are listed 'Best First'.
Re: An arithmetic oddity
by ikegami (Patriarch) on Aug 31, 2009 at 20:00 UTC

    Just been asked: int($x) gives strange result

    1/10 and 3/10 are periodic numbers in binary, just like 1/3 is a periodic number in decimal. They can't be represented exactly by a floating point number.

Re: An arithmetic oddity
by JavaFan (Canon) on Aug 31, 2009 at 19:58 UTC
    You are assuming binary computers can represent any decimal fraction exactly. They cannot (natively). Basically, what you are seeing are rounding errors.

    Please read the FAQ for more details.

Re: An arithmetic oddity
by bv (Friar) on Aug 31, 2009 at 20:05 UTC

    It's a problem of integer arithmetic and changing bases: http://docs.sun.com/source/806-3568/ncg_goldberg.html for the details. For a quick idea, try this:

    $ perl -e 'printf "%.20f\n",(0.3-0.1)*10' 1.99999999999999977796
    print pack("A25",pack("V*",map{1919242272+$_}(34481450,-49737472,6228,0,-285028276,6979,-1380265972)))
Re: An arithmetic oddity
by jxb (Initiate) on Sep 01, 2009 at 01:29 UTC
    Thanks all.

    I'd suspected a roundoff issue, and thought I'd see evidence of this by using 'print' or 'printf' on $a[2] (without specifying how many decimal places). Since I got 2 or 2.000000, I discarded that thought. But specifying several decimal places out (i.e. 'printf("%.20f", $a[2])' ) reveals what the problem is.

    jxb