Reputation: 13842
I have 2 variables, x, y with "numeric" data. Note, both of these come from different sources (mysql data and parsed file data), so I am assuming firstly that they have ended up as strings.
## this will work fine with any data that I assign manually, so can't reproduce the issue for you here...
## $x sourced from an sql query, $y sourced from a parsed file
warn "test x == y, $x == $y is " . ($x == $y);
warn "test x != y, $x != $y is " . ($x != $y);
warn "test x eq y, $x eq $y is " . ($x eq $y);
warn "test x ne y, $x ne $y is " . ($x ne $y);
warn "unpack x, " . unpack 'H*', $x;
warn "unpack y, " . unpack 'H*', $y;
output
test x == y, 14 == 14 is
test x != y, 14 != 14 is 1
test x eq y, 14 eq 14 is 1
test x ne y, 14 ne 14 is
unpack x, 3134
unpack y, 3134
Unless I'm having a bad hair day, which is possible. The first two tests "appear" incorrect, as I would expect the strings to be converted to numbers and compared.
I can get around it by using "ne" rather than !=, but I want to try and understand, so I don't introduce bugs elsewhere as there's some issue I don't understand.
Note: If I assign 14 or "14" to $y to test beforehand, all works as I'd expect. Doing the same with $x makes no difference, so I assume something is "funny" with $y. If I assign $y = $y + 0 or similar to make sure it's a number, it makes no difference.
So as I can't reproduce the example nicely, I'm trying to understand what may be wrong, and how I can isolate what is "different" about $y or poke about at it.
Edit: If I Devel::Peek at $x (doesn't change after comparison)
SV = PVNV(0xe2de4c0) at 0x101ccad8
REFCNT = 1
FLAGS = (IOK,NOK,POK,pIOK,pNOK,pPOK)
IV = 14
NV = 14
PV = 0x447ab580 "14"\0
CUR = 2
LEN = 10
If I Devel::Peek at $y
SV = PVNV(0xcf94880) at 0xf039770
REFCNT = 1
FLAGS = (NOK,pIOK,pNOK)
IV = 14
NV = 14
PV = 0
I also note:
warn ($x - $y);
output: -1.77635683940025e-15
Which suggests choroba is maybe correct (although why does $y need stringifying for a numeric comparison, I can see that may happen when adding to a string in debug, but not in the original). I'm a little surprised by the whole thing though, in both the fact it doesn't display the float, or any easy way to figure if that was the case.
Edit2: Interestingly, this only happens for the number 14 in the test data, number 12 for example gives the correct result, which again may make sense if there is some rounding happening. I've got around it by $x=int($x + 0.5) and $y=int($y + 0.5) as that's actually what I want the values to be.
Edit: ikegamis sprintf on $y produces
14.0000000000000017763568394002504646778106689453125
Just out of interest, I traced this all the way back to a XML::LibXML::Reader find_value() call, where the number was originally a 0.14 string and multiplied, so my mistake for not doing some proper data checking in the code.
Shouldn't Devel::Peek show the number as 14.0000000000000017763568394002504646778106689453125 under the hood ?
Edit: Just for those interested, I can manually replicate it now with setting the following, which makes the problem a bit more obvious.
my $x = "0.14";
$x *= 100;
my $y = 14;
Upvotes: 4
Views: 140
Reputation: 241848
my $x = 14.000000000000001;
my $y = 14;
When stringifying a value, Perl rounds the number a bit if needed.
See also What Every Computer Scientist Should Know About Floating-Point Arithmetic
Upvotes: 2