Perl / perl5

🐪 The Perl programming language
https://dev.perl.org/perl5/
Other
1.85k stars 527 forks source link

is this a bug? #6569

Closed p5pRT closed 20 years ago

p5pRT commented 20 years ago

Migrated from rt.perl.org#22725 (status was 'resolved')

Searchable as RT22725$

p5pRT commented 20 years ago

From giswty@nus.edu.sg

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

p5pRT commented 20 years ago

From Robin.Barker@npl.co.uk

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.


p5pRT commented 20 years ago

From @demerphq

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

p5pRT commented 20 years ago

From Robin.Barker@npl.co.uk

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.


p5pRT commented 20 years ago

From @mhx

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

p5pRT commented 20 years ago

From @mhx

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&#8203;:\\>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&#8203;: %22\.20lf\\n"\, x\); 
    \} 

    C&#8203;:\\>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&#8203;: 1\.00000000000000000000 

    C&#8203;:\\>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&#8203;: 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

p5pRT commented 20 years ago

From @pjcj

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

p5pRT commented 20 years ago

From perl5-porters@ton.iguana.be

In article \18286\.193\.134\.254\.145\.1055950103\.squirrel@&#8203;wesley\.pjcj\.net\,   "Paul Johnson" \paul@&#8203;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.'

p5pRT commented 20 years ago

From @demerphq

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

p5pRT commented 20 years ago

@rspier - Status changed from 'new' to 'resolved'

p5pRT commented 20 years ago

From @pjcj

On Wed\, Jun 18\, 2003 at 04​:25​:45PM +0000\, Ton Hospel wrote​:

In article \18286\.193\.134\.254\.145\.1055950103\.squirrel@&#8203;wesley\.pjcj\.net\, "Paul Johnson" \paul@&#8203;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

p5pRT commented 20 years ago

@rgs - Status changed from 'open' to 'resolved'

p5pRT commented 20 years ago

From nick.ing-simmons@elixent.com

Marie Vivian Wong Tzu Yenn \perl5\-porters@&#8203;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/

p5pRT commented 20 years ago

From acme@astray.com

Very amusing discussion but not a bug.

p5pRT commented 20 years ago

acme@astray.com - Status changed from 'open' to 'resolved'