Perl / perl5

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

power-of two differences in minus operation #8859

Open p5pRT opened 17 years ago

p5pRT commented 17 years ago

Migrated from rt.perl.org#42260 (status was 'open')

Searchable as RT42260$

p5pRT commented 17 years ago

From jan-perl@h-i-s.nl

Created by jan-perl@h-i-s.nl

The minus operator creates strange results for the given example. Using bignum or bigint does not change the results. For the given program\, running perl v5.6.1 on an old pentium-90 gave the expected output in the last two colums​: the samen value as in the first column\, and zero respectively. The example program reads a multiplier from the first line\, and then verifies the relation $val[4] == $val[0] * mult + $val[1]

example program​:

#!/usr/bin/perl -w

$mul=\; $mul+=0; $hdrlin=\;   print "${mul}\n${hdrlin}"; while ($readlin = \ ) {   @​vals =split(/\t/\,$readlin);   my $diff= ($vals[4] - $vals[1] ) - $vals[0]*$mul;   my $v1m=sprintf ("%.0f"\,$vals[0]*$mul);   my $v1c=sprintf ("%.0f"\,($vals[0]*$mul+$vals[1]));   my $d1=sprintf ("%.0f"\, ($vals[4] - $vals[1] ) );   print "$vals[0]\t$vals[1]\t$v1m\t$v1c\t$vals[4]\t$d1\t$diff\n"; }

example input​:

4294967296 v1 v2 v1m v1c v1g d1 diff 104712103 50 449735057880383488 449735057880383552 449735057880383538 0 -64 108391849 154 465539446607970304 465539446607970432 465539446607970458 192 64 108391849 159 465539446607970304 465539446607970432 465539446607970463 192 64 108391849 219 465539446607970304 465539446607970496 465539446607970523 256 64 112424657 21 482860225079017472 482860225079017472 482860225079017493 64 64 112424657 25 482860225079017472 482860225079017472 482860225079017497 64 64 113036199 53 485486777969147904 485486777969147968 485486777969147957 128 64 113036199 56 485486777969147904 485486777969147968 485486777969147960 128 64 114286490 102 490856736924631040 490856736924631168 490856736924631142 64 -64 114286490 105 490856736924631040 490856736924631168 490856736924631145 64 -64 114286490 108 490856736924631040 490856736924631168 490856736924631148 64 -64 114286490 114 490856736924631040 490856736924631168 490856736924631154 64 -64 114286490 98 490856736924631040 490856736924631168 490856736924631138 64 -64 116114550 207 498708194839756800 498708194839756992 498708194839757007 256 64 1454437274 498 6246760525913391104 6246760525913391104 6246760525913391602 1024 1024 1685221006 5 7237969107302219776 7237969107302219776 7237969107302219781 0 -1024 1689676942 5 7257107206695288832 7257107206695288832 7257107206695288837 0 1024

example output (WRONG\, from given perl version)

4294967296 v1 v2 v1m v1c v1g d1 diff 104712103 50 449735057880383488 449735057880383552 449735057880383538 449735057880383424 -64 108391849 154 465539446607970304 465539446607970432 465539446607970458 465539446607970368 64 108391849 159 465539446607970304 465539446607970432 465539446607970463 465539446607970368 64 108391849 219 465539446607970304 465539446607970496 465539446607970523 465539446607970368 64 112424657 21 482860225079017472 482860225079017472 482860225079017493 482860225079017536 64 112424657 25 482860225079017472 482860225079017472 482860225079017497 482860225079017536 64 113036199 53 485486777969147904 485486777969147968 485486777969147957 485486777969147968 64 113036199 56 485486777969147904 485486777969147968 485486777969147960 485486777969147968 64 114286490 102 490856736924631040 490856736924631168 490856736924631142 490856736924630976 -64 114286490 105 490856736924631040 490856736924631168 490856736924631145 490856736924630976 -64 114286490 108 490856736924631040 490856736924631168 490856736924631148 490856736924630976 -64 114286490 114 490856736924631040 490856736924631168 490856736924631154 490856736924630976 -64 114286490 98 490856736924631040 490856736924631168 490856736924631138 490856736924630976 -64 116114550 207 498708194839756800 498708194839756992 498708194839757007 498708194839756864 64 1454437274 498 6246760525913391104 6246760525913391104 6246760525913391602 6246760525913392128 1024 1685221006 5 7237969107302219776 7237969107302219776 7237969107302219781 7237969107302218752 -1024 1689676942 5 7257107206695288832 7257107206695288832 7257107206695288837 7257107206695289856 1024

Correct output (as from the old perl on the old computer)

4294967296 v1 v2 v1m v1c v1g d1 diff 104712103 50 449735057880383488 449735057880383552 449735057880383538 449735057880383488 0 108391849 154 465539446607970304 465539446607970432 465539446607970458 465539446607970304 0 108391849 159 465539446607970304 465539446607970432 465539446607970463 465539446607970304 0 108391849 219 465539446607970304 465539446607970496 465539446607970523 465539446607970304 0 112424657 21 482860225079017472 482860225079017472 482860225079017493 482860225079017472 0 112424657 25 482860225079017472 482860225079017472 482860225079017497 482860225079017472 0 113036199 53 485486777969147904 485486777969147968 485486777969147957 485486777969147904 0 113036199 56 485486777969147904 485486777969147968 485486777969147960 485486777969147904 0 114286490 102 490856736924631040 490856736924631168 490856736924631142 490856736924631040 0 114286490 105 490856736924631040 490856736924631168 490856736924631145 490856736924631040 0 114286490 108 490856736924631040 490856736924631168 490856736924631148 490856736924631040 0 114286490 114 490856736924631040 490856736924631168 490856736924631154 490856736924631040 0 114286490 98 490856736924631040 490856736924631168 490856736924631138 490856736924631040 0 116114550 207 498708194839756800 498708194839756992 498708194839757007 498708194839756800 0 1454437274 498 6246760525913391104 6246760525913391104 6246760525913391602 6246760525913391104 0 1685221006 5 7237969107302219776 7237969107302219776 7237969107302219781 7237969107302219776 0 1689676942 5 7257107206695288832 7257107206695288832 7257107206695288837 7257107206695288832 0

Perl Info ``` Flags: category=core severity=high Site configuration information for perl v5.8.8: Configured by Debian Project at Fri Jul 7 18:20:22 UTC 2006. Summary of my perl5 (revision 5 version 8 subversion 8) configuration: Platform: osname=linux, osvers=2.6.15.7, archname=i486-linux-gnu-thread-multi uname='linux rothera 2.6.15.7 #1 smp tue jun 27 18:34:43 utc 2006 i686 gnulinux ' config_args='-Dusethreads -Duselargefiles -Dccflags=-DDEBIAN -Dcccdlflags=-fPIC -Darchname=i486-linux-gnu -Dprefix=/usr -Dprivlib=/usr/share/perl/5.8 -Darchlib=/usr/lib/perl/5.8 -Dvendorprefix=/usr -Dvendorlib=/usr/share/perl5 -Dvendorarch=/usr/lib/perl5 -Dsiteprefix=/usr/local -Dsitelib=/usr/local/share/perl/5.8.8 -Dsitearch=/usr/local/lib/perl/5.8.8 -Dman1dir=/usr/share/man/man1 -Dman3dir=/usr/share/man/man3 -Dsiteman1dir=/usr/local/man/man1 -Dsiteman3dir=/usr/local/man/man3 -Dman1ext=1 -Dman3ext=3perl -Dpager=/usr/bin/sensible-pager -Uafs -Ud_csh -Uusesfio -Uusenm -Duseshrplib -Dlibperl=libperl.so.5.8.8 -Dd_dosuid -des' hint=recommended, useposix=true, d_sigaction=define usethreads=define use5005threads=undef useithreads=define usemultiplicity=define useperlio=define d_sfio=undef uselargefiles=define usesocks=undef use64bitint=undef use64bitall=undef uselongdouble=undef usemymalloc=n, bincompat5005=undef Compiler: cc='cc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -DTHREADS_HAVE_PIDS -DDEBIAN -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64', optimize='-O2', cppflags='-D_REENTRANT -D_GNU_SOURCE -DTHREADS_HAVE_PIDS -DDEBIAN -fno-strict-aliasing -pipe -I/usr/local/include' ccversion='', gccversion='4.1.2 20060613 (prerelease) (Ubuntu 4.1.1-2ubuntu5)', gccosandvers='' intsize=4, longsize=4, ptrsize=4, doublesize=8, byteorder=1234 d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=12 ivtype='long', ivsize=4, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8 alignbytes=4, prototype=define Linker and Libraries: ld='cc', ldflags =' -L/usr/local/lib' libpth=/usr/local/lib /lib /usr/lib libs=-lgdbm -lgdbm_compat -ldb -ldl -lm -lpthread -lc -lcrypt perllibs=-ldl -lm -lpthread -lc -lcrypt libc=/lib/libc-2.4.so, so=so, useshrplib=true, libperl=libperl.so.5.8.8 gnulibc_version='2.4' Dynamic Linking: dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E' cccdlflags='-fPIC', lddlflags='-shared -L/usr/local/lib' Locally applied patches: @INC for perl v5.8.8: /etc/perl /usr/local/lib/perl/5.8.8 /usr/local/share/perl/5.8.8 /usr/lib/perl5 /usr/share/perl5 /usr/lib/perl/5.8 /usr/share/perl/5.8 /usr/local/lib/site_perl . Environment for perl v5.8.8: HOME=/home/jhh LANG=en_US.UTF-8 LANGUAGE=en_NL:en LD_LIBRARY_PATH (unset) LOGDIR (unset) PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/bin/X11:/usr/games PERL_BADLANG (unset) SHELL=/bin/bash ```
p5pRT commented 17 years ago

From @doughera88

On Sat\, 31 Mar 2007\, jhh wrote​:

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

The minus operator creates strange results for the given example.

Here's a much shorter example that illustrates the same problem. What you are encountering is the finite resolution of perl's (and C's) floating point numbers. If you really require 18-digit accuracy\, then perl's built-in numbers are not the right tool.

You are correct that the exact details of rounding off in the last bit have changed between 5.6 and 5.8\, but in neither version should they have been considered reliable.

Using bignum or bigint does not change the results.

That's odd. You'll have to show us specifically what you're doing. It's probably best if you modify the program below and show what you expect and what you get. BigInt should work.

#!/usr/bin/perl -w my $mul = 2**32; my $a = 104712103; my $b = 50; my $c = 449735057880383538; # For these values\, $mul * $a + $b == $c. Thus $diff should be zero.
my $diff = $c - ($a * $mul + $b); printf "Expected​: $a $b 449735057880383538 0\n"; printf "Got​: %.0f %.0f %.0f %.0f\n"\, $a\, $b\, $c\, $diff;

__END__

# Actual output (Solaris/SPARC\, 32-bit integers)​:

$ perl5.6.2 bugreport Expected​: 104712103 50 449735057880383538 0 Got​: 104712103 50 449735057880383552 0

$ perl5.8.8 bugreport Expected​: 104712103 50 449735057880383538 0 Got​: 104712103 50 449735057880383488 -64

Note too that for both 5.6 and 5.8\, the value of $c rendered by printf "%.0f" is wrong.

--   Andy Dougherty doughera@​lafayette.edu

p5pRT commented 17 years ago

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

p5pRT commented 17 years ago

From @doughera88

On Mon\, 9 Apr 2007\, Tels wrote​:

-----BEGIN PGP SIGNED MESSAGE----- Hash​: SHA1

Moin\,

On Monday 09 April 2007 19​:00​:56 Andy Dougherty wrote​:

On Sat\, 31 Mar 2007\, jhh wrote​:

Using bignum or bigint does not change the results.

That's odd. You'll have to show us specifically what you're doing. It's probably best if you modify the program below and show what you expect and what you get. BigInt should work.

When using "-Mbignum"\, the diff will be zero\, but the printout of $c will be wrong since %0.f is used\, which converts the BigInt to a normal floating point value before printing\, thus losing bits​:

Yes. I agree. That's why I said the original poster would have to show us specifically what he was doing.

You are also correct that the wrong number is still printed by printf("%.0f"). However\, the wrong number also was printed with perl5.6 and that apparently wasn't an important issue for the original poster. (Why it didn't matter\, I don't know\, but then I don't know the full context of the original problem.)

--   Andy Dougherty doughera@​lafayette.edu

p5pRT commented 17 years ago

From nospam-abuse@bloodgate.com

-----BEGIN PGP SIGNED MESSAGE----- Hash​: SHA1

Moin\,

On Monday 09 April 2007 19​:00​:56 Andy Dougherty wrote​:

On Sat\, 31 Mar 2007\, jhh wrote​:

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

The minus operator creates strange results for the given example.

Here's a much shorter example that illustrates the same problem. What you are encountering is the finite resolution of perl's (and C's) floating point numbers. If you really require 18-digit accuracy\, then perl's built-in numbers are not the right tool.

You are correct that the exact details of rounding off in the last bit have changed between 5.6 and 5.8\, but in neither version should they have been considered reliable.

Using bignum or bigint does not change the results.

That's odd. You'll have to show us specifically what you're doing. It's probably best if you modify the program below and show what you expect and what you get. BigInt should work.

When using "-Mbignum"\, the diff will be zero\, but the printout of $c will be wrong since %0.f is used\, which converts the BigInt to a normal floating point value before printing\, thus losing bits​:

  #!/usr/bin/perl -w   my $mul = 2**32;   my $a = 104712103;   my $b = 50;   my $c = 449735057880383538;   # For these values\, $mul * $a + $b == $c.  Thus $diff should be zero.   my $diff = $c - ($a * $mul + $b);   printf "Expected​: $a $b 449735057880383538 0\n";   printf "Got​:      %.0f %.0f %.0f %.0f\n"\, $a\, $b\, $c\, $diff;   printf "\$c $c %0.f\n"\, $c;

Result (5.8.8 on 64bit\, thus diff is 0 even without bignum)​:

  # perl acc.pl   Expected​: 104712103 50 449735057880383538 0   Got​:      104712103 50 449735057880383552 0   $c 449735057880383538 449735057880383552   # perl -Mbignum acc.pl   Expected​: 104712103 50 449735057880383538 0   Got​:      104712103 50 449735057880383552 0   $c 449735057880383538 449735057880383552

I am not sure if printf("%0.f"\, $bignumber) should actually truncate the result\, or just print it in all its glory. Can this even be achieved with overloading?

All the best\,

Tels

- -- Signed on Mon Apr 9 21​:11​:42 2007 with key 0x93B84C15. View my photo gallery​: http​://bloodgate.com/photos PGP key on http​://bloodgate.com/tels.asc or per email.

"The need for a Steam account to play HL2 is like having to login to MS Passport to play Minesweeper."

  -- Tels -----BEGIN PGP SIGNATURE----- Version​: GnuPG v1.4.2 (GNU/Linux)

iQEVAwUBRhqtJHcLPEOTuEwVAQIJhQf7BPTehno/agg+Qzw1tQkdL/dXFKLuwXby PLggxcjanVjn4q3w5rg+FiJURPONQAOHPPgR8i8aYMYnXHd+WfsK4raVi2AGw7LL jKOhyLwQBywNGvWhrEn8LFvid69CKIkIBUorLuj4i0NaxFAhoC8tKg3/fFxt6z5u woMwdeUuMfBPi8iwOW+5GlVvYpQSp19WdflSaQhjv5LBFV2MdtnpYNiqX41DpzNt H3KmhF3gZyD5R5CJvLYeQj+OrVjAWSjJ/pUoevJBlLU6O9EB1ujx6HLK6lV13zaU fryvoNSQQpdiqM2ggGreccvPjyd7paAIO3e003sh8E2/k/sjE/xOpA== =+9+y -----END PGP SIGNATURE-----

p5pRT commented 17 years ago

From jan-perl@h-i-s.nl

What I really am trying to do is to verify the numbers are right​: it is output of an embedded software system running on another computer platform. Thus\, the only real issue for me is if the $diff variable actually goes to zero\, as it did in the older versions.

I did now check with bignum on\, and actually get larger diffs -- perl -Mbignum ./chk2.pl \<prcmult3.txt >prcmult8bn.txt

I am not familiar enough with perl to change operator overloading etc. I am familiar with C and data types\, however. I suspect that forcing the reading routine to generate 64 bit integers would allow the reproduction of the results of your code examples.

4294967296 v1 v2 v1m v1c v1g d1 diff 104712103 50 449735057880383488 449735057880383552 449735057880383538 449735057880383424 -488 108391849 154 465539446607970304 465539446607970432 465539446607970458 465539446607970368 -304 108391849 159 465539446607970304 465539446607970432 465539446607970463 465539446607970368 -304 108391849 219 465539446607970304 465539446607970496 465539446607970523 465539446607970368 -304 112424657 21 482860225079017472 482860225079017472 482860225079017493 482860225079017536 528 112424657 25 482860225079017472 482860225079017472 482860225079017497 482860225079017536 528 113036199 53 485486777969147904 485486777969147968 485486777969147957 485486777969147968 96 113036199 56 485486777969147904 485486777969147968 485486777969147960 485486777969147968 96 114286490 102 490856736924631040 490856736924631168 490856736924631142 490856736924630976 -40 114286490 105 490856736924631040 490856736924631168 490856736924631145 490856736924630976 -40 114286490 108 490856736924631040 490856736924631168 490856736924631148 490856736924630976 -40 114286490 114 490856736924631040 490856736924631168 490856736924631154 490856736924630976 -40 114286490 98 490856736924631040 490856736924631168 490856736924631138 490856736924630976 -40 116114550 207 498708194839756800 498708194839756992 498708194839757007 498708194839756864 200 1454437274 498 6246760525913391104 6246760525913391104 6246760525913391602 6246760525913392128 -1104 1685221006 5 7237969107302219776 7237969107302219776 7237969107302219781 7237969107302218752 224 1689676942 5 7257107206695288832 7257107206695288832 7257107206695288837 7257107206695289856 1168

p5pRT commented 17 years ago

From jan-perl@h-i-s.nl

I have posted a follow-up via the bug-tracking tool. I hope you get an update that way.

-- Jan Hoogenraad Hoogenraad Interface Services Postbus 2717 3500 GS Utrecht

p5pRT commented 8 years ago

From @dcollinsn

Is there any more to this ticket than "floats have limited precision"? There's some discussion about printf('%f') under Bignum\, but my understanding is that the %f format specifier is supposed to treat the input as a float.

-- Respectfully\, Dan Collins

p5pRT commented 8 years ago

From @sisyphus

-----Original Message----- From​: Dan Collins via RT Sent​: Saturday\, July 16\, 2016 12​:56 PM To​: OtherRecipients of perl Ticket #42260​: Cc​: perl5-porters@​perl.org Subject​: [perl #42260] power-of two differences in minus operation

Is there any more to this ticket than "floats have limited precision"? There's some discussion about printf('%f') under Bignum\, but my understanding is that the %f format specifier is supposed to treat the input as a float.

I think this crosses over into #41202 territory.

I find that perl is assigning 449735057880383538 incorrectly - to internal hex format of 4398f71e9c000000 (instead of 4398f71e9c000001). And I think that's happening to the op as well.

For the script​:

my $mul = 2**32; my $a = 104712103; my $b = 50; my $c = 449735057880383538; # For these values\, $mul * $a + $b == $c. Thus $diff should be zero. my $diff = $c - ($a * $mul + $b); printf "Expected​: $a $b 449735057880383538 0\n"; printf "Got​: %.0f %.0f %.0f %.0f\n"\, $a\, $b\, $c\, $diff; printf "\$c $c %0.f\n"\, $c;

I get outputs of (perl-5.16.0)​:

Expected​: 104712103 50 449735057880383538 0 Got​: 104712103 50 449735057880383488 -64 $c 4.49735057880383e+017 449735057880383488

and (perl-5.24.0)​:

Expected​: 104712103 50 449735057880383538 0 Got​: 104712103 50 449735057880383552 0 $c 449735057880383538 449735057880383550

In both of those perls "my $c = 449735057880383538;" has assigned 4398f71e9c000000 to $c.

Cheers\, Rob