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

I've found certain triplets of numbers that added up and put in a variable are, equal to 1, print as 1, are true on looks_like_number, match 1 when evaluated with eq but do not match 1 using using ==.

The order the numbers are added matters, the script below produces output in the form.

0.688 + 0.289 + 0.023
total is 1
looks like a number
fails on ==
matches on eq

0.688 + 0.023 + 0.289
total is 1
looks like a number
matches on ==
matches on eq

0.559 + 0.380 + 0.061
total is 1
looks like a number
matches on ==
matches on eq

#!/usr/bin/perl
use strict;
use warnings;
use Scalar::Util qw(looks_like_number);


while (<DATA>) {
    if (m{^#}) {
	print;
	next;
    }
    chomp;
    my ($x, $y, $z) = split(m{ });
#   my $var = $x + $y + $z;
    my $var = $x;
    $var += $y;
    $var += $z;
    
    print "$x + $y + $z\n";
    print "total is $var\n";
    if (looks_like_number($var)) { 
	print "looks like a number\n";
    }
    else {
	print "doesn't look like a number\n"
    }
    if ($var == 1) {
	print "matches on ==\n";
    }
    else {
	print "fails on ==\n";
    }
    if ($var eq 1) {
	print "matches on eq\n";
    }
    else {
	print "fails on eq\n"
    }
    print "\n";
}

__DATA__
#FAIL
0.688 0.289 0.023
0.500 0.422 0.078
0.693 0.290 0.017
0.207 0.563 0.230
0.491 0.421 0.088
0.498 0.420 0.082
0.696 0.285 0.019
0.693 0.286 0.021
0.517 0.409 0.074
# ORDER CHANGED 
0.688 0.023 0.289
0.422 0.078 0.500
# PASS
0.559 0.380 0.061
0.648 0.314 0.038
0.546 0.414 0.040
0.600 0.348 0.052
0.653 0.311 0.036
0.741 0.245 0.014
0.787 0.201 0.012
0.651 0.318 0.031
0.627 0.331 0.042

Replies are listed 'Best First'.
Re: $var == 1 fails when $var = 1
by LanX (Archbishop) on Sep 20, 2018 at 15:08 UTC
    floating-point numbers have rounding errors and our decimal system isn't too compatible to binary representation

    DB<8> printf '%.20f', 0.688 + 0.289 + 0.023 0.99999999999999989000

    so $var != 1

    Perl's DWIM is sometimes trying to round the string representation, that's why eq seemingly "works".

    Rule of thumb, add integers and correct afterwards to the right magnitude.

    DB<15> p 1 == (688 + 289 + 23) /1000 1

    see also Humans have too many fingers

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

      Why does the order of addition matter?
      DB<8> printf '%.20f', 0.688
      0.68799999999999994493
      DB<9> printf '%.20f', 0.688 + 0.023
      0.71099999999999996536
      DB<10> printf '%.20f', 0.688 + 0.023  + 0.289
      1.00000000000000000000
      DB<11> printf '%.20f', 0.688 + 0.289 + 0.023
      0.99999999999999988898
      
      PS and thanks for the prompt answer!
        Because you are accumulating different rounding errors and (most likely) a shifting mantissa changes the last bit to be rounded.

        update

        Rounding is often not very intuitive, try to avoid if possible.

        If you want to track the details you need to look into the binary representation and the underlying C lib.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

Re: $var == 1 fails when $var = 1
by hippo (Chancellor) on Sep 20, 2018 at 15:09 UTC
Re: $var == 1 fails when $var = 1 (lost post)
by LanX (Archbishop) on Sep 20, 2018 at 23:44 UTC
      Well once again

      Why ?

      Does that link actually answer either of the 2 issues raised by the OP ?
      Those 2 issues can be paraphrased as:

      1) Why does perl assert that $x is 1 when $x != 1 ?
      2) How is it that $x+$y+$z != $x+$z+$y ?

      Cheers,
      Rob
        primarily b/c I was worried about the lost post.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery FootballPerl is like chess, only without the dice

      o_0 I saw and upvoted that reply from Laurent_R too…

Re: $var == 1 fails when $var = 1
by Anonymous Monk on Sep 20, 2018 at 16:36 UTC

    Math::BigFloat will make these calculations precise, though slower.

    my $var = Math::BigFloat->new($x) + Math::BigFloat->new($y) + Math::BigFloat->new($z);

    Just make sure to pass them to the Math::BigFloat constructor while they are still strings. If you use them as numbers first you could lose the precision before it can be stored.