Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

[perl #22725] is this a bug?

2 views
Skip to first unread message

Marie Vivian Wong Tzu Yenn

unread,
Jun 17, 2003, 11:26:28 PM6/17/03
to bugs-bi...@netlabs.develooper.com
# 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...@gmx.net

Robin Barker

unread,
Jun 18, 2003, 8:18:27 AM6/18/03
to perl5-...@perl.org

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


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.
-------------------------------------------------------------------

Yves Orton

unread,
Jun 18, 2003, 9:24:33 AM6/18/03
to perl5-...@perl.org, marie...@gmx.net
> 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

Robin Barker

unread,
Jun 18, 2003, 9:41:49 AM6/18/03
to perl5-...@perl.org
I wrote:
> see C<perldoc -f decimal> for a full explanation.
I meant:
perldoc -q decimal

Robin

Marcus Holland-Moritz

unread,
Jun 18, 2003, 10:24:41 AM6/18/03
to Orton, Yves, perl5-...@perl.org, marie...@gmx.net
> 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

Marcus Holland-Moritz

unread,
Jun 18, 2003, 11:19:13 AM6/18/03
to Orton, Yves, perl5-...@perl.org
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


Paul Johnson

unread,
Jun 18, 2003, 11:28:23 AM6/18/03
to Orton, Yves, perl5-...@perl.org, marie...@gmx.net

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 - pa...@pjcj.net
http://www.pjcj.net

Ton Hospel

unread,
Jun 18, 2003, 12:25:45 PM6/18/03
to perl5-...@perl.org
In article <18286.193.134.254.14...@wesley.pjcj.net>,

"Paul Johnson" <pa...@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.'

Yves Orton

unread,
Jun 18, 2003, 11:29:49 AM6/18/03
to Paul Johnson, perl5-...@perl.org, marie...@gmx.net
> 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

Paul Johnson

unread,
Jun 18, 2003, 2:12:24 PM6/18/03
to Ton Hospel, perl5-...@perl.org
On Wed, Jun 18, 2003 at 04:25:45PM +0000, Ton Hospel wrote:
> In article <18286.193.134.254.14...@wesley.pjcj.net>,
> "Paul Johnson" <pa...@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

Nick Ing-Simmons

unread,
Jun 19, 2003, 5:08:44 AM6/19/03
to perl5-...@perl.org
Marie Vivian Wong Tzu Yenn <perl5-...@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/


0 new messages