Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Bug? 1+1 != 2

by shotgunefx (Parson)
on Jun 19, 2003 at 06:26 UTC ( [id://267095]=perlquestion: print w/replies, xml ) Need Help??

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

I've run across something strange. I've got to think it's my sleep deprived stupidity but is seems I have a script where 1+1 isn't computing on both Linux & Windows (5.6.1). It works if I change to eq but isn't it odd that they stringify the same?
Here's the test case.
#!/usr/bin/perl use strict; use warnings; my $base = 0.0425; while ( my $line = <DATA>){ chomp $line; my ($z,$c1,$c2,$c3,$c4) = split(/,/,$line); print join("|",$z,$c1,$c2,$c3,$c4),"\n"; my $total = $c1+$c2+$c3; print $c4 == ($total + $base) ? "True\n" : "False $c4 != ".($total+ +$base)."\n"; } __DATA__ 00501,0.0000,0.0425,0.0025,0.0875 00544,0.0000,0.0425,0.0025,0.0875 06390,0.0000,0.0425,0.0025,0.0875
This outputs

00501|0.0000|0.0425|0.0025|0.0875
False 0.0875 != 0.0875
00544|0.0000|0.0425|0.0025|0.0875
False 0.0875 != 0.0875
06390|0.0000|0.0425|0.0025|0.0875
False 0.0875 != 0.0875


-Lee

"To be civilized is to deny one's nature."

Replies are listed 'Best First'.
Re: Bug? 1+1 != 2
by BrowserUk (Patriarch) on Jun 19, 2003 at 07:17 UTC

    This is the classic rounding error. Using printf to display a few more significant digits from the intermediate terms shows up what is happening:

    #!/usr/bin/perl use strict; use warnings; my $base = 0.0425; while ( my $line = <DATA>){ chomp $line; my ($z,$c1,$c2,$c3,$c4) = split(/,/,$line); print join("|",$z,$c1,$c2,$c3,$c4),"\n"; my $total = $c1+$c2+$c3; printf '%32.32f + %32.32f ?==? %32.32f'.$/, $base, $total, $total + +$base; print $c4 == ($total + $base) ? "True\n" : "False $c4 != ".($total+ +$base)."\n"; } __DATA__ 00501,0.0000,0.0425,0.0025,0.0875 00544,0.0000,0.0425,0.0025,0.0875 06390,0.0000,0.0425,0.0025,0.0875

    Which produces this output

    D:\Perl\test>test 00501|0.0000|0.0425|0.0025|0.0875 0.04250000000000000300000000000000 + 0.0450000000000000050000000000000 +0 ?==? 0.08750000000000000800000000000000 False 0.0875 != 0.0875 00544|0.0000|0.0425|0.0025|0.0875 0.04250000000000000300000000000000 + 0.0450000000000000050000000000000 +0 ?==? 0.08750000000000000800000000000000 False 0.0875 != 0.0875 06390|0.0000|0.0425|0.0025|0.0875 0.04250000000000000300000000000000 + 0.0450000000000000050000000000000 +0 ?==? 0.08750000000000000800000000000000 False 0.0875 != 0.0875

    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller


      Interesting ...I don't see a solution for the gent here, it looks like a bug to me too perhaps in the interal storage of floats?

        It's not a bug per se. It is an inherent limitation of using floating point math on a computer. At least those that use the IEEE standard for their floating point representation.

        Essentially, floating point stores decimal numbers as binary encoded fractions. That is to say 5/8ths (0.625 decimal) is stored as (0.)101 in binary. The 0 and the decimal (actually binary) point aren't really stored as they can be implied. The '101' translates to

        1 x 1/2 + 0 x 1/4 + 1 x 1/8 ----------- 5/8 = 0.625

        which is great because it means that decimal fraction 0.625 can be represented exactly as a binary fraction, so no loss of accuracy.

        However, many decimal fractions cannot be represented exactly in binary. This is the same as trying to express 1/3 as a decimal fraction. The best you can do is 0.333... but when you multiply 0.333... * 3 you get 0.999... No matter how many decimal places you add, it never quite adds up to 1.00 as it should. The same is true when it comes to representing many decimal fractions in binary. The representation is inexact. Eg. 0.1 decimal in binary

        1/2 (0.500000000000) 0 1/4 (0.250000000000) 0 1/8 (0.125000000000) 0 1/16 (0.062500000000) 1 0.0625 1/32 (0.031250000000) 1 0.03125 1/64 (0.015625000000) 0 1/128 (0.007812500000) 0 1/256 (0.003906250000) 1 0.00390625 1/512 (0.001953125000) 0 1/1024 (0.000976562500) 1 0.0009765625 1/2048 (0.000488281250) 0 1/4096 (0.000244140625) 0 --------------------------------------------------- 0.0986328125

        As you can see, using 12 significant digits (bits) of binary fraction (the limits of the display on my calculator:), representing decimal 0.1 is pretty poor. Luckily, perl uses many more (52) bits and so it can achieve much better accuracy.

        perl> printf "%.32f\n", 0.1 0.100 000 000 000 000 010 000 000 000 000 00

        But it is still not exact and never will be no matter how many more binary digits (bits) you used.

        But to put that in perspective, the inaccuracy shown is one tenth, of one thousandth, of one billionth of whatever it is you are measuring.

        In monetary terms, that's a 10 cents (dime or a nickel? (Who cares:)) in a trillion dollars.

        In terms of the human population, that's one or maybe two of your eyelashes, from the whole mass of the human species.

        Or another way of looking at it, its 10 microns of inaccuracy on the distance from here to the Moon.

        For most everyday purposes, it's "close enough":)


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller


Re: Bug? 1+1 != 2
by fglock (Vicar) on Jun 19, 2003 at 06:45 UTC

    They are not exactly the same. There is a very small rounding error:

    #!/usr/bin/perl use strict; use warnings; my $base = 0.0425; while ( my $line = <DATA>){ chomp $line; my ($z,$c1,$c2,$c3,$c4) = split(/,/,$line); print join("|",$z,$c1,$c2,$c3,$c4),"\n"; my $total = $c1+$c2+$c3; print $c4 == ($total + $base) ? "True\n" : "False ". sprintf("%20.18f",$c4)." != ". sprintf("%20.18f",($total+$base))."\n"; } __DATA__ 00501,0.0000,0.0425,0.0025,0.0875 00544,0.0000,0.0425,0.0025,0.0875 06390,0.0000,0.0425,0.0025,0.0875

    output:

    00501|0.0000|0.0425|0.0025|0.0875 False 0.087499999999999994 != 0.087500000000000008 00544|0.0000|0.0425|0.0025|0.0875 False 0.087499999999999994 != 0.087500000000000008 06390|0.0000|0.0425|0.0025|0.0875 False 0.087499999999999994 != 0.087500000000000008
      I figured as much, though I am still curious about the stringified representations. I've never thought too deeply about it. How does Perl determine the formatting for numbers when stringifying? $#?

      -Lee

      "To be civilized is to deny one's nature."
Re: Bug? 1+1 != 2
by helgi (Hermit) on Jun 19, 2003 at 09:29 UTC
    The problem is pretty simple, as others have stated and is due to the rounding off of floating point numbers.

    The real problem is however, that this is a Very Frequently Asked Question and as such, should be answered in the FAQ.

    Of course the question in perlfaq4:

    perldoc -q Why am I getting long decimals (eg, 19.9499999999999) inste +ad of the numbers I should be getting (eg, 19.95)?
    in essence answers this question, but it could be indexed with a different heading, something like:
    Why don't my floating point numbers equal each other?


    --
    Regards,
    Helgi Briem
    helgi DOT briem AT decode DOT is

      I figured it was along those lines as "eq" worked. Any idea on how Perl decides to format the stringified number? I looked at $# but it says it's depreciated so I would think it lies elsewhere.

      -Lee

      "To be civilized is to deny one's nature."
        The internal decision about how Perl "stringifies" floats are quite well described in perlnumber; if you need different conversions, you have to resort to printf and explicit rounding.
Re: Bug? 1+1 != 2
by bobn (Chaplain) on Jun 19, 2003 at 06:54 UTC
    I don't get it. It doesn't look like a logic issue, though.

    issuing :%s/0\.0/0./g while editing in vi, writing and executing yields:
    #!/usr/bin/perl use strict; use warnings; my $base = 0.425; while ( my $line = <DATA>){ chomp $line; my ($z,$c1,$c2,$c3,$c4) = split(/,/,$line); print join("|",$z,$c1,$c2,$c3,$c4),"\n"; my $total = $c1+$c2+$c3; print $c4 == ($total + $base) ? "True\n" : "False $c4 != ".($total+$ +base)."\n"; } __DATA__ 00501,0.000,0.425,0.025,0.875 00544,0.000,0.425,0.025,0.875 06390,0.000,0.425,0.025,0.875
    with results:
    00501|0.000|0.425|0.025|0.875 True 00544|0.000|0.425|0.025|0.875 True 06390|0.000|0.425|0.025|0.875 True
    So making everything 10 times bigger yields expected results. Rounding error?

    --Bob Niederman, http://bob-n.com

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://267095]
Approved by Tanalis
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others imbibing at the Monastery: (5)
As of 2024-04-16 16:44 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found