Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Comparing results of math operations

by stangoesagain (Novice)
on Apr 16, 2014 at 09:12 UTC ( #1082463=perlquestion: print w/ replies, xml ) Need Help??
stangoesagain has asked for the wisdom of the Perl Monks concerning the following question:

Little real life problem
#!/usr/bin/perl use strict; use warnings; use 5.010; my $x=0.95*806; my $y=1.3*589; say $x <=> $y; $x == $y ? say 'equal' : say $x.' '.$y; say $x cmp $y; $x eq $y ? say 'equal' : say $x.' '.$y; #now with simpler math $x=2*1.25; $y=5*0.5; say $x <=> $y; $x == $y ? say 'equal' : say $x.' '.$y; say $x cmp $y; $x eq $y ? say 'equal' : say $x.' '.$y;
gives
-1 765.7 765.7 0 equal 0 equal 0 equal
Why? What's wrong?

Comment on Comparing results of math operations
Select or Download Code
Re: Comparing results of math operations
by Laurent_R (Parson) on Apr 16, 2014 at 09:26 UTC
    This is a usual difficulty with floating point number arithmetics (with most languages). In your first example, try this:
    my $x = 0.95*806; my $y = 1.3*589; print $y - $x; # prints something like : 1.13686837721616e-13
    So, although the numbers seem to be equal when rounded to 1 or even 5 decimal places, they are not really equal. Even if the numbers were mathematically exactly equal, the simple fact that you derived them from arithmetical operations on other source floating point numbers may lead to different results, because the original numbers are first converted to an internal binary representation which may lead to some internal rounding even before the multiplication is applied.

    The lesson is: don't compare for equality floating point numbers derived from arithmetic operations; rather subtract one from the other and check if the absolute value of the difference is smaller than a certain predetermined very small number.

Re: Comparing results of math operations
by zentara (Archbishop) on Apr 16, 2014 at 09:50 UTC
    Here is another textbook example which demonstrates the problem. You need to specify how many digits of accuracy you need for the comparison, and round your floats off accordingly.
    #!/usr/bin/perl -w use strict; my ( $number, $premium, $expected ); $number = 1.80; $premium = $number * ( 1 + 10/100 ); # 1.8 + 10% of 1.8 $expected = 1.98; # As we know 1.8 + 10% of 1.8 is 1.98 printf "Number 1 : %20.19f\n", $premium ; printf "Number 2 : %20.19f\n", $expected ; print "Not" if ( $expected != $premium ); print "Equal !! "; __END__ Number 1 : 1.9800000000000002043 Number 2 : 1.9799999999999999822 NotEqual !!

    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
Re: Comparing results of math operations (try integer)
by LanX (Canon) on Apr 16, 2014 at 09:55 UTC
    Better avoid decimal fractions!

    They can't always be transformed loss-free to binary fractions (see Re: eq vs ==)

    But this works:

    DB<107> $x=95*806 /100; => "765.7" DB<108> $y=13*589 /10; => "765.7" DB<109> $x <=> $y => 0

    So just calculate with integers and shift the decimal point afterwards!

    The deeper reason for this mess is that Humans have too many fingers ;-)

    Cheers Rolf

    ( addicted to the Perl Programming Language)

Re: Comparing results of math operations
by hippo (Curate) on Apr 16, 2014 at 10:30 UTC

    This and all the usual resulting questions are explained very well in the Numbers section of the FAQ. I would strongly advise you to read all the way through that section if computing with floating point numbers is new to you (which it would appear to be). Good luck.

Re: Comparing results of math operations
by Anonymous Monk on Apr 16, 2014 at 12:11 UTC
    Floating-point number like little pile of sand on the ground. Each time you pick it up and move it around, you lose a little sand (precision) and pick up a little dirt (error). Rounding and such masks this effect but does not get rid of it. Add up enough numbers and you will see (to the vexation of your accountant) that the total at the bottom is "off by a few cents." In fact, the total is correct, because it is the sum of non-rounded numbers, whereas the check performed by the calculator-toting accountant is the sum of rounded numbers.
Re: Comparing results of math operations
by syphilis (Canon) on Apr 16, 2014 at 12:43 UTC
    Hi,
    The essential difference between the first assignments to $x and $y, and the assignments made later (in the "#now with simpler math" section) is that while 2, 1.25, 5, and 0.5 can *all* be represented exactly in base 2, the same cannot be said of 0.95, 806, 1.3 and 589.
    Sure ... both 806 and 589 can be represented exactly in base 2, but 0.95 and 1.3 cannot.

    Hence the first calculations do not yield "expected" values, whereas the second calculations *do* yield expected values.

    Workarounds include doing base 10 calculations - eg by using Math::Decimal (pure perl) or Math::Decimal64 (XS access to the C compiler's _Decimal64 arithmetic).

    Cheers,
    Rob
      I agree that the transformation from decimal to binary fractions is the biggest source of problems.

      But does switching to decimal floats solve all problems?

      1/3 can't be represented loss-free in neither decimal nor binary floats (3 is not a primefactor of 10 or 2)

      So (1/3 * 3) can be different to 1.

      Though it isn't ATM

      DB<125> 1/3 *3 <=> 1 => 0

      I'm not sure if that's always the case for similar calculations.

      I remember similar discussions talking about something like "symbolic calculations", where divisor and denominator are kept separate till the end.

      Cheers Rolf

      ( addicted to the Perl Programming Language)

        discussions talking about something like "symbolic calculations", where divisor and denominator are kept separate till the end
        Do you mean Math::Fraction?
        لսႽÜ ᥲᥒ⚪⟊Ⴙᘓᖇ Ꮅᘓᖇ⎱ Ⴙᥲ𝇋ƙᘓᖇ
        But does switching to decimal floats solve all problems?

        No ... but then rational arithmetic (where you keep divisor and numerator separate) also fails to solve all problems, as it falls down when you start to deal with irrational numbers such as sqrt(2) or transcendentals such as pi.

        I think what drew me to decimal arithmetic was that the values that appeared in the OP's post were all *exactly* representable in base 10, as were the results of the calculations he presented.
        If there had been a "1 / 3" (or some other rational value that couldn't be exactly represented in base 10) in that post then I would more likely have been drawn to modules such as Math::BigRat or Math::GMPq.

        Cheers,
        Rob
Re: Comparing results of math operations
by AnomalousMonk (Abbot) on Apr 16, 2014 at 13:43 UTC
Re: Comparing results of math operations
by Anonymous Monk on Apr 17, 2014 at 07:59 UTC

    The 0.95 * 806, 1.3 * 589 example; gives me

    765.699999999999932 765.700000000000045

    when I use the 10-digit calculator trick of subtracting off the leading digits and multiplying by 10, to see the hidden digit off to the right.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://1082463]
Front-paged by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others surveying the Monastery: (6)
As of 2014-10-25 19:13 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    For retirement, I am banking on:










    Results (148 votes), past polls