Problems? Is your data what you think it is? PerlMonks

\$var == 1 fails when \$var = 1

by mjlush (Novice)
 on Sep 20, 2018 at 15:01 UTC Need Help??

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 (Sage) 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

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 (Bishop) on Sep 20, 2018 at 15:09 UTC
Re: \$var == 1 fails when \$var = 1 (lost post)
by LanX (Sage) on Sep 20, 2018 at 23:44 UTC

Well once again.

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

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.

Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1222720]
Approved by LanX
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others drinking their drinks and smoking their pipes about the Monastery: (4)
As of 2022-08-16 14:08 GMT
Sections?
Information?
Find Nodes?
Leftovers?
Voting Booth?

No recent polls found

Notices?