Closed p5pRT closed 20 years ago
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_wong@gmx.net
This is not a bug\, see C\<perldoc -f decimal> for a full explanation.
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 -----Original Message----- From: Marie Vivian Wong Tzu Yenn [mailto:perlbug-followup@perl.org] Sent: 18 June 2003 04:26 To: perl5-porters@perl.org Subject: [perl #22725] is this a bug?
# New Ticket Created by "Marie Vivian Wong Tzu Yenn" # Please include the string: [perl #22725] # in the subject line of all future correspondence about this issue. # \<URL: http://rt.perl.org/rt2/Ticket/Display.html?id=22725 >
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_wong@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.
of numbers (0.4+0.3+0.2+0.1)\, it does not add up to one. (falls to the else loop). ... If I change these numbers to like 0.25+0.5+0.25 OR 0.9+0.1 etc then no problem.
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
I wrote:
see C\<perldoc \-f decimal> for a full explanation\.
I meant: perldoc -q decimal
Robin
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.
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.
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
RE: [perl #22725] is this a bug?> > 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.
Ok\, im still confused. Is the mantissa responsible for the difference in these:
C​:\\>type addnums\.cpp // AddNums\.cpp : Defines the entry point for the console application\. // \#include "stdafx\.h" \#include \<stdio\.h> \#include \<stdlib\.h> int \_tmain\(int argc\, \_TCHAR\* argv\[\]\) \{ double x = 0\.0; while \(\-\-argc\) \{ double val = atof\( \*\+\+argv \); x \+= val; printf\("Add : %22\.20lf\\tSum :%22\.20lf\\n"\, val\,x\); \} printf\("Final​: %22\.20lf\\n"\, x\); \} C​:\\>addnums 0\.1 0\.2 0\.3 0\.4 Add : 0\.10000000000000001000 Sum :0\.10000000000000001000 Add : 0\.20000000000000001000 Sum :0\.30000000000000004000 Add : 0\.29999999999999999000 Sum :0\.60000000000000009000 Add : 0\.40000000000000002000 Sum :1\.00000000000000000000 Final​: 1\.00000000000000000000 C​:\\>addnums 0\.4 0\.3 0\.2 0\.1 Add : 0\.40000000000000002000 Sum :0\.40000000000000002000 Add : 0\.29999999999999999000 Sum :0\.69999999999999996000 Add : 0\.20000000000000001000 Sum :0\.89999999999999991000 Add : 0\.10000000000000001000 Sum :0\.99999999999999989000 Final​: 0\.99999999999999989000
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
Orton\, Yves said:
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 - paul@pjcj.net http://www.pjcj.net
In article \18286\.193\.134\.254\.145\.1055950103\.squirrel@​wesley\.pjcj\.net\, "Paul Johnson" \paul@​pjcj\.net writes:
Orton\, Yves said:
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;
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.'
Here's a patch for the bug:
./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
@rspier - Status changed from 'new' to 'resolved'
On Wed\, Jun 18\, 2003 at 04:25:45PM +0000\, Ton Hospel wrote:
In article \18286\.193\.134\.254\.145\.1055950103\.squirrel@​wesley\.pjcj\.net\, "Paul Johnson" \paul@​pjcj\.net writes:
Orton\, Yves said:
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;
No. That would be a cure worse than the disease. Many floating point
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
-- Paul Johnson - paul@pjcj.net http://www.pjcj.net
@rgs - Status changed from 'open' to 'resolved'
Marie Vivian Wong Tzu Yenn \perl5\-porters@​perl\.org writes:
# New Ticket Created by "Marie Vivian Wong Tzu Yenn" Please include the # string: [perl #22725] in the subject line of all future correspondence about # this issue. \<URL: http://rt.perl.org/rt2/Ticket/Display.html?id=22725 >
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
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/
Very amusing discussion but not a bug.
acme@astray.com - Status changed from 'open' to 'resolved'
Migrated from rt.perl.org#22725 (status was 'resolved')
Searchable as RT22725$