Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

Strange (rounding?) problem

by gildir (Pilgrim)
on May 02, 2001 at 13:56 UTC ( [id://77275]=perlquestion: print w/replies, xml ) Need Help??

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

Fellow monks,
I encountered a problem, that looks very strange at first glance:
warn $obj1->value; warn $obj2->value; warn ($obj1->value == $obj2->value);
This code will produce these results: 739.3, 739.3 and false.
So we modified the last line of code:
warn ($obj1->value - $obj2->value);
There was a meaningful result: 1.13686837721616e-13

Now I understand that these two numbers are in fact different, but why does warn $number display the wrong (rounded) value? How can I control this behavior of warn?

What I was trying to achieve is a warning message like 'The numbers X and Y are different!' but it looks more than little stupid to display message 'The numbers 739.3 and 739.3 are different!'.

Replies are listed 'Best First'.
Re: Strange (rounding?) problem
by iakobski (Pilgrim) on May 02, 2001 at 14:41 UTC
    When programming in a strongly-typed language, you get it drummed in at an early age that you never, never compare floating point values for equality. So C coders might invent some way of representing how close they expect the values to be to be as good as the same, for example you might say that 0.1% difference is not worth worrying about: (pseudocode)
    if ( (( x - y ) / x ) * (( x - y ) / x ) > 0.001 * 0.001 ){ ## they are different }
    Now Perl with its weak typing lulls us into a sense of security, especially with behind the scenes extras like only converting the sensible part of a floating point number into a string. So instead of the shenanigans of the strongly typed language, use Perl's behind the scenes extras by comparing the strings:
    print "$x and $y are different\n" if ( $x ne $y );
    You will see you don't even need to coerce the values to strings, let alone printf format them, just use eq and ne directly!
    iakobski

      A ++ vote just isn't enough. I have to reply to help call attention to this neat trick (using ne and eq to compare floating-point numbers).

      Thanks!

              - tye (but my friends call me "Tye")
Re: Strange (rounding?) problem
by jeroenes (Priest) on May 02, 2001 at 14:02 UTC
(stephen) Re: Strange (rounding?) problem
by stephen (Priest) on May 02, 2001 at 14:31 UTC
    I can't think of a better way to explain it than perlfaq 4:
    When a floating-point number gets printed, the binary floating-point representation is converted back to decimal. These decimal numbers are displayed in either the format you specify with printf(), or the current output format for numbers (see $# if you use print. $# has a different default value in Perl5 than it did in Perl4. Changing $# yourself is deprecated.

    This affects all computer languages that represent decimal floating-point numbers in binary, not just Perl. Perl provides arbitrary-precision decimal numbers with the Math::BigFloat module (part of the standard Perl distribution), but mathematical operations are consequently slower.

    To get rid of the superfluous digits, just use a format (eg, printf("%.2f", 19.95)) to get the required precision. See Floating-point Arithmetic.

    stephen

Re: Strange (rounding?) problem
by alfie (Pilgrim) on May 02, 2001 at 14:07 UTC
    You need to define how much digits you count as significant. There are lots of examples where it's noted that you can't directly compare floats with each other. Do something along this lines:
    print "The numbers $foo and $bar are significant different!" if (abs($foo - $bar) >= (10**-10));
    Adjust the power to your likes.

    Update: Of course it should be >=, thanks Eureka_sg. Somehow I seemed to have thought about using unless but wrote if instead...
    --
    use signature; signature(" So long\nAlfie");

      I believe what you are trying to say is:

      print "The numbers $foo and $bar are significant different!" if (abs($foo - $bar) >= (10**-10));

      Note the difference in the equality sign. : )

        The problem with this is that you have to know a priori what the range of your numbers is.

        suppose $foo = 1e-11 and $bar = 2e-11 your method will say they are not significantly different.

        Suppose $foo = 1.1e54 and $bar = 1.1e54 then you would want to say that they are not different, but it is quite possible that a stray bit would cause your method to say that they are different.

        iakobski

Re: Strange (rounding?) problem
by Beatnik (Parson) on May 02, 2001 at 15:58 UTC
    Something like

    for($i=0; $i!= 10 ; $i += 0.10) { print $i; }

    will loop forever coz of the floating point :)
    Don't try this at home kids.

    Greetz
    Beatnik
    ... Quidquid perl dictum sit, altum viditur.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others meditating upon the Monastery: (6)
As of 2024-04-19 12:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found