Perl / perl5

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

inconsistent printf decimal handling #7214

Closed p5pRT closed 20 years ago

p5pRT commented 20 years ago

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

Searchable as RT28267$

p5pRT commented 20 years ago

From greg_trott@raytheon.com

This is a bug report for perl from greg_trott@​raytheon.com\, generated with the help of perlbug 1.33 running under perl v5.6.1.


printf is inconsistent in its handling of decimal integer output. It seems to round certain values but not others. For example​:

################################################## #!/usr/bin/perl -w

  $thisval = (2.01)*(10**2);   printf "thisval decimal = %d\n"\, $thisval;

  $thisval = (2.02)*(10**2);   printf "thisval decimal = %d\n"\, $thisval;

  $thisval = (2.03)*(10**2);   printf "thisval decimal = %d\n"\, $thisval;

  $thisval = (2.04)*(10**2);   printf "thisval decimal = %d\n"\, $thisval;

  $thisval = (3.01)*(10**2);   printf "thisval decimal = %d\n"\, $thisval; ##################################################

Output​:

thisval decimal = 200 thisval decimal = 202 thisval decimal = 202 thisval decimal = 204 thisval decimal = 301

Expected output​:

thisval decimal = 201 thisval decimal = 202 thisval decimal = 203 thisval decimal = 204 thisval decimal = 301

I've seen this problem using perl v5.6.1 on Solaris (see below) and also using v5.8.3 on Windows 2000.



Flags​:   category=core   severity=medium


Site configuration information for perl v5.6.1​:

Configured by smcadams at Tue Feb 26 16​:33​:08 EST 2002.

Summary of my perl5 (revision 5.0 version 6 subversion 1) configuration​:   Platform​:   osname=solaris\, osvers=2.5.1\, archname=sun4-solaris-thread-multi   uname='sunos slpyyh 5.5.1 generic_103640-39 sun4u sparc sunw\,ultra-5_10 '   config_args='-Dusethreads -Dprefix=/usr/site/perl-5.6.1'   hint=recommended\, useposix=true\, d_sigaction=define   usethreads=define use5005threads=undef useithreads=define usemultiplicity=define   useperlio=undef d_sfio=undef uselargefiles=define usesocks=undef   use64bitint=undef use64bitall=undef uselongdouble=undef   Compiler​:   cc='gcc'\, ccflags =' '\,   optimize='-O'\,   cppflags=''   ccversion=''\, gccversion='2.8.1'\, gccosandvers='solaris2.6'   intsize=4\, longsize=4\, ptrsize=4\, doublesize=8\, byteorder=4321   d_longlong=define\, longlongsize=8\, d_longdbl=define\, longdblsize=16   ivtype='long'\, ivsize=4\, nvtype='double'\, nvsize=8\, Off_t='off_t'\, lseeksize=4   alignbytes=8\, usemymalloc=y\, prototype=define   Linker and Libraries​:   ld='gcc'\, ldflags =' '   libpth=/usr/lib   libs=-lsocket -lnsl -ldl -lm -lposix4 -lpthread -lc   perllibs=-lsocket -lnsl -ldl -lm -lposix4 -lpthread -lc   libc=/lib/libc.so\, so=so\, useshrplib=false\, libperl=libperl.a   Dynamic Linking​:   dlsrc=dl_dlopen.xs\, dlext=so\, d_dlsymun=undef\, ccdlflags=' '   cccdlflags='-fPIC'\, lddlflags='-G'

Locally applied patches​:  


@​INC for perl v5.6.1​:   /usr/site/perl-5.6.1/lib/5.6.1/sun4-solaris-thread-multi   /usr/site/perl-5.6.1/lib/5.6.1   /usr/site/perl-5.6.1/lib/site_perl/5.6.1/sun4-solaris-thread-multi   /usr/site/perl-5.6.1/lib/site_perl/5.6.1   /usr/site/perl-5.6.1/lib/site_perl   .


Environment for perl v5.6.1​:   HOME=/home/oatmealu2/gtrott   LANG=C   LANGUAGE (unset)   LD_LIBRARY_PATH=/usr/lib​:/usr/local/lib​:/usr/dt/lib​:/usr/openwin/lib​:/usr/lib/X11​:/usr/X11R5/lib​:./   LOGDIR (unset)   PATH=/usr/sbin​:/usr/j2se/bin​:/usr/bin​:/usr/openwin/bin​:/usr/local/bin​:/opt/SUNWspro/bin​:/usr/ucb​:/usr/etc​:/usr​:/usr/bin/X11​:/bin​:/usr/budtool/bin​:/usr/atria/bin​:/usr/local/flexlm/bin​:/usr/ccs/bin​:/home/oatmealu2/gtrott/bin​:.​:/home/oatmealu2/gtrott/CROP/ant.sun/apache-ant-1.5.3-1/bin   PERL_BADLANG (unset)   SHELL=/bin/csh

p5pRT commented 20 years ago

From @mhx

Thank you for your report.

However\, this is not a bug. It's a frequent problem when dealing with floating point numbers. Most floating point numbers\, such as 2.01\, do not have an exact binary representation​:

mhx@​r2d2 $ perl -e 'printf "%.20f\n"\, $_ for 2.01\, 2.02\, 2.03\, 2.04' 2.00999999999999978684 2.02000000000000001776 2.02999999999999980460 2.04000000000000003553

(When you're doing arithmetics on floating point values\, chances are good that they lose even more precision.)

Additionally\, you're using the %d format. This format represents a signed integer value (see perldoc -f sprintf). As your value is a floating point value\, it is forced into an integer value before printing. This is _not_ done by rounding\, but by truncating the floating point value. Thus your "wrong" results.

To get your expected results\, use the %f format. You can specify a precision in number of decimal (0 in your case)\, and this will actually round the values​:

mhx@​r2d2 $ perl -e 'printf "%.0f\n"\, (10**2)*$_ for 2.01\, 2.02\, 2.03\, 2.04' 201 202 203 204

Marcus

p5pRT commented 20 years ago

The RT System itself - Status changed from 'new' to 'open'

p5pRT commented 20 years ago

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

p5pRT commented 20 years ago

From rjk@penkwe.pair.com

On Mon\, Apr 05\, 2004 at 06​:29​:05PM -0000\, greg_trott@​raytheon.com (via RT) wrote​:

# New Ticket Created by greg_trott@​raytheon.com # Please include the string​: [perl #28267] # in the subject line of all future correspondence about this issue. # \<URL​: http​://rt.perl.org​:80/rt3/Ticket/Display.html?id=28267 >

This is a bug report for perl from greg_trott@​raytheon.com\, generated with the help of perlbug 1.33 running under perl v5.6.1.

----------------------------------------------------------------- printf is inconsistent in its handling of decimal integer output. It seems to round certain values but not others. For example​:

################################################## #!/usr/bin/perl -w

$thisval = (2.01)*(10**2); printf "thisval decimal = %d\n"\, $thisval;

$thisval = (2.02)*(10**2); printf "thisval decimal = %d\n"\, $thisval;

$thisval = (2.03)*(10**2); printf "thisval decimal = %d\n"\, $thisval;

$thisval = (2.04)*(10**2); printf "thisval decimal = %d\n"\, $thisval;

$thisval = (3.01)*(10**2); printf "thisval decimal = %d\n"\, $thisval; ##################################################

Thank you for your report. However\, this is not a bug. First\, %d does not round\, it truncates. If you want to round\, you should use %f. Second\, perlfaq4 explains why floating point numbers may not behave as you expect​:

  Why am I getting long decimals (eg\, 19.9499999999999) instead of the   numbers I should be getting (eg\, 19.95)?

  Internally\, your computer represents floating-point numbers in   binary. Digital (as in powers of two) computers cannot store all   numbers exactly. Some real numbers lose precision in the process.   This is a problem with how computers store numbers and affects all   computer languages\, not just Perl.

  perlnumber show the gory details of number representations and   conversions.

  To limit the number of decimal places in your numbers\, you can use   the printf or sprintf function. See the "Floating Point Arithmetic"   for more details.

  printf "%.2f"\, 10/3;

  my $number = sprintf "%.2f"\, 10/3;

HTH\, Ronald