Hi there,
I'm new to perl and was writing a small program. Found something funny. When I try to add this particular sequence of numbers (0.4+0.3+0.2+0.1), it does not add up to one. (falls to the else loop).
The version of perl compiler I am using is perl 5.6.1 on windows and solaris
If I change these numbers to like 0.25+0.5+0.25 OR 0.9+0.1 etc then no problem.
Here is the code snippet:
*************************
use strict;
use warnings;
my $tmp = 0.4+0.3+0.2+0.1;
if ($tmp == 1) {
print "Equals one", "\n";
}
else {
print "Not equal to one", "\n";
}
exit;
thanks!
Marie
email: marie...@gmx.net
If you add a line after 'print "Not equal to one", "\n";'
printf "\$tmp = %.20f\n", $tmp;
the output is:
Not equal to one
$tmp = 0.99999999999999988898
Robin
Hi there,
thanks!
Marie
email: marie...@gmx.net
-------------------------------------------------------------------
This e-mail and any attachments may contain confidential and/or
privileged material; it is for the intended addressee(s) only.
If you are not a named addressee, you must not use, retain or
disclose such information.
NPL Management Ltd cannot guarantee that the e-mail or any
attachments are free from viruses.
NPL Management Ltd. Registered in England and Wales. No: 2937881
Registered Office: Teddington, Middlesex, United Kingdom TW11 0LW.
-------------------------------------------------------------------
At first I thought this was a classic example of rounding problems. Assuming
that the binary representation of that sequence when summed doesnt actually
equal 1. As it would appear can be seen here:
D:\Development>perl -e "printf '%22.20f',0.4+0.3+0.2+0.1"
0.99999999999999989000
Whereas the binary representation of the other examples (0.9,0.1 and
0.25+0.25+0.5) do add up to 1:
D:\Development>perl -e "printf '%22.20f',0.25+0.5+0.25"
1.00000000000000000000
D:\Development>perl -e "printf '%22.20f',0.9+0.1"
1.00000000000000000000
But the below makes me think that this is indeed a bug.
D:\Development>perl -e "printf '%22.20f',0.4
0.40000000000000002000
D:\Development>perl -e "printf '%22.20f',0.3
0.29999999999999999000
D:\Development>perl -e "printf '%22.20f',0.2
0.20000000000000001000
D:\Development>perl -e "printf '%22.20f',0.1
0.10000000000000001000
When I take these results and sum them in my handy dandy desk calculator I
get
1.00000000000000003
So now im not sure that this isnt a bug. But i dont know where it lies, or
to what degree we can trust the printf output.
However, the GOOD news is that your problem can be worked around by simply
using the string 'eq' and not the numerical ==.
D:\Development>perl -e "print 0.4+0.3+0.2+0.1 == 1 ? 'Equals' : 'NotEquals'"
NotEquals
D:\Development>perl -e "print 0.4+0.3+0.2+0.1 eq 1 ? 'Equals' : 'NotEquals'"
Equals
Ill leave it up to somebody else to determine if it is in fact a bug, and/or
what the cause of the discrepancy between the outputs.
Yves
Robin
Nope, no bug here. Just the classic rounding problem:
E:\>type add.c
#include <stdio.h>
#include <stdlib.h>
int main( int argc, char **argv )
{
double x = 0.0;
while (--argc)
x += atof( *++argv );
printf("%22.20lf\n", x);
}
E:\>.\add 0.4 0.3 0.2 0.1
0.99999999999999989000
E:\>perl -e"printf qq'%22.20f\n', 0.4+0.3+0.2+0.1"
0.99999999999999989000
Remember, the precision of a "double" mantissa is only
53 bits, i.e. about 1e-16:
0.00000 00000 00000 1
0.99999 99999 99999 89000
So the error is quite ok after 3 floating point operations.
However, the easiest workaround is obviously to just write
the operands in the correct order:
E:\>perl -e"printf qq'%22.20f\n', 0.4+0.2+0.1+0.3"
1.00000000000000000000
;-)
-- Marcus
Yes.
E:\>type float.pl
$a = 1.0e-16;
$b = 0.0;
$b += $a for 1 .. 100000;
$b += 1.0;
printf "%22.20f\n", $b;
$b = 1.0;
$b += $a for 1 .. 100000;
printf "%22.20f\n", $b;
E:\>perl float.pl
1.00000000001000000000
1.00000000000000000000
As you can see, initializing $b to zero and adding 1e-16 for 100000
times can be done quite exact in IEEE math. It's because the numbers
are all just about the same magnitude. It does not matter if you're
dealing with numbers between 1 and 1000 or between 1e-100 and 1e-103.
But adding 1 and 1e-100 is impossible.
That's what the second example shows: initializing $b to one and
adding 1e-16 for 100000 times has no effect at all. Just because
1.0000000000000001 cannot be represented by a double precision float.
> And thanks for the follow up, I appreciate it.
>
> BTW, the above makes me think that its safer to sum floats inorder. Would this be correct?
Although I'm not an expert (so don't blame me if I'm wrong ;-) I'd
say that adding floats should be more precise when you're doing it
in ascending order of the absolute values.
-- Marcus
> Yves
> D:\Development>perl -e "print 0.4+0.3+0.2+0.1 == 1 ? 'Equals' :
> 'NotEquals'"
> NotEquals
> Ill leave it up to somebody else to determine if it is in fact a bug,
> and/or what the cause of the discrepancy between the outputs.
Here's a patch for the bug:
*** pp_hot.c.org Thu Jul 18 23:32:07 2002
--- pp_hot.c Wed Jun 18 17:04:43 2003
***************
*** 287,292 ****
--- 287,295 ----
#endif
{
dPOPnv;
+ if (ckWARN(WARN_NUMERIC))
+ Perl_warner(aTHX_ packWARN(WARN_NUMERIC),
+ "Comparing equality of floating point values");
SETs(boolSV(TOPn == value));
RETURN;
}
*** pp.c.org Tue Jun 18 22:26:49 2002
--- pp.c Wed Jun 18 17:09:17 2003
***************
*** 1953,1958 ****
--- 1953,1961 ----
#endif
{
dPOPnv;
+ if (ckWARN(WARN_NUMERIC))
+ Perl_warner(aTHX_ packWARN(WARN_NUMERIC),
+ "Comparing equality of floating point values");
SETs(boolSV(TOPn != value));
RETURN;
}
./perl -we "print 0.4+0.3+0.2+0.1 == 1 ? 'Equals' : 'NotEquals'"
Comparing equality of floating point values at -e line 1.
NotEquals
--
Paul Johnson - pa...@pjcj.net
http://www.pjcj.net
No. That would be a cure worse than the disease. Many floating point
operations are 100% predictabl and it can be perfectly ok to use ==
on them, e.g:
perl -wle '$a=2.; print "yes" if $a+3. == 5.'
> ./perl -we "print 0.4+0.3+0.2+0.1 == 1 ? 'Equals' : 'NotEquals'"
> Comparing equality of floating point values at -e line 1.
> NotEquals
Nice!
Yves
In case anyone was unsure, the patch was not meant to be applied.
> operations are 100% predictabl and it can be perfectly ok to use ==
> on them, e.g:
>
> perl -wle '$a=2.; print "yes" if $a+3. == 5.'
However, this case would not have triggered the warning:
./perl -wle '$a=2.; print "yes" if $a+3. == 5.'
yes
./perl -wle '$a=2.1; print "yes" if $a+3.1 == 5.2'
Comparing equality of floating point values at -e line 1.
yes
This isn't a perl problem it is a generic floating point problem common
to all computers and languages.
The problem is that just as you cannot represent 1/6 or 1/3 as an exact
decimal form, computers cannot represent 0.1 (1/10) as an exact binary
form. So this fails for same reason 0.333 + 0.333 + 0.333 != 1
>
>
>If I change these numbers to like 0.25+0.5+0.25 OR 0.9+0.1 etc then no
>problem.
0.25 and 0.5 are exact in binary. 0.9 and 0.1 are like 0.667 + 0.333
and happen to work.
--
Nick Ing-Simmons http://www.ni-s.u-net.com/