Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

64-bit Integers -- inexact division gives odd result when is large #9316

Open
p5pRT opened this issue May 6, 2008 · 11 comments
Open

64-bit Integers -- inexact division gives odd result when is large #9316

p5pRT opened this issue May 6, 2008 · 11 comments

Comments

@p5pRT
Copy link

p5pRT commented May 6, 2008

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

Searchable as RT53784$

@p5pRT
Copy link
Author

p5pRT commented May 6, 2008

From chris.hall@highwayman.com

Created by chris.hall@highwayman.com

Division of a large 64-bit integer by a small integer gives peculiar
(broken) result when the division is inexact.

Consider​:

  idiv(0x0FED_CBA9_8765_4321, 9, 9) ;

  print "_" x 70, "\n" ;

  idiv(1<<52, 3, 2) ;

  sub idiv {
  my ($z, $j, $b) = @​_ ;

  printf "a = 0x%016X * $b + i, for i = 0..%d\n", $z, $j ;
  print " i​: d = int(a/$b) : a/$b :",
  " a%$b : a - d*$b\n" ;

  foreach my $i (0..$j) {
  my $a = ($z * $b) + $i ;
  my $q = $a / $b ;
  my $d = int($q) ;
  my $r = $a % $b ;
  my $s = $a - ($d * $b) ;

  printf " %2d​: 0x%016X : %20s : %+3d : %+5d\n", $i, $d, "$q", $r, $s ;
  } ;
  } ;

which on an x86-64 produces​:

  a = 0x0FEDCBA987654321 * 9 + i, for i = 0..9
  i​: d = int(a/9) : a/9 : a%9 : a - d*9
  0​: 0x0FEDCBA987654321 : 1147797409030816545 : +0 : +0
  1​: 0x0FEDCBA987654380 : 1.14779740903082e+18 : +1 : -854
  2​: 0x0FEDCBA987654380 : 1.14779740903082e+18 : +2 : -853
  3​: 0x0FEDCBA987654380 : 1.14779740903082e+18 : +3 : -852
  4​: 0x0FEDCBA987654380 : 1.14779740903082e+18 : +4 : -851
  5​: 0x0FEDCBA987654380 : 1.14779740903082e+18 : +5 : -850
  6​: 0x0FEDCBA987654380 : 1.14779740903082e+18 : +6 : -849
  7​: 0x0FEDCBA987654380 : 1.14779740903082e+18 : +7 : -848
  8​: 0x0FEDCBA987654380 : 1.14779740903082e+18 : +8 : -847
  9​: 0x0FEDCBA987654322 : 1147797409030816546 : +0 : +0
  ______________________________________________________________________
  a = 0x0010000000000000 * 2 + i, for i = 0..3
  i​: d = int(a/2) : a/2 : a%2 : a - d*2
  0​: 0x0010000000000000 : 4503599627370496 : +0 : +0
  1​: 0x0010000000000000 : 4503599627370496 : +1 : +1
  2​: 0x0010000000000001 : 4503599627370497 : +0 : +0
  3​: 0x0010000000000002 : 4503599627370498 : +1 : -1

The first section shows the effect. Where the integer division is exact we
get the right integer result. Where it is not exact, the result is floated,
with hopeless loss of significance !

I assume that the code runs an integer division, and when the remainder is
not zero it floats the result of the division and then adds
remainder / divisor.

The second section shows what happens when the result of the division is
53 bits of integer (same as precision of the floating point), plus fraction
when the result is not exact. Note the effect of Round-to-Even !

I suggest that the code should be extended to check if the result is

= max integer in floating form. If it is, then the result should be left
in integer form, and​: if remainder is > divisor/2 round up, or
  if remainder == divisor/2, round to even.

(As shown above, the % operator is giving the right result. So all is
not lost.)

[FWIW​: Round-to-Even is all very well, but in this case truncation would
  seem a better choice -- all inexact integer divisions would then
  always yield the integer result + fraction (when precision allows).]

Perl Info

Flags:
     category=core
     severity=high

This perlbug was built using Perl v5.8.8 in the Red Hat build system.
It is being executed now by Perl v5.8.8 - Thu Mar 20 04:50:07 EDT 2008.

Site configuration information for perl v5.8.8:

Configured by Red Hat, Inc. at Thu Mar 20 04:50:07 EDT 2008.

Summary of my perl5 (revision 5 version 8 subversion 8) configuration:
   Platform:
     osname=linux, osvers=2.6.18-53.1.6.el5xen, archname=x86_64-linux-thread-multi
     uname='linux xenbuilder2.fedora.redhat.com 2.6.18-53.1.6.el5xen #1 smp wed jan 16 04:10:44 est 2008 x86_64 x86_64 x86_64 gnulinux '
     config_args='-des -Doptimize=-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -
mtune=generic -Dversion=5.8.8 -Dmyhostname=localhost -Dperladmin=root@localhost -Dcc=gcc -Dcf_by=Red Hat, Inc. -Dinstallprefix=/usr -
Dprefix=/usr -Dlibpth=/usr/local/lib64 /lib64 /usr/lib64 -Dprivlib=/usr/lib/perl5/5.8.8 -Dsitelib=/usr/lib/perl5/site_perl/5.8.8 -Dvendorlib=/us
r/lib/perl5/vendor_perl/5.8.8 -Darchlib=/usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi -Dsitearch=/usr/lib64/perl5/site_perl/5.8.8/x86_64-linu
x-thread-multi -Dvendorarch=/usr/lib64/perl5/vendor_perl/5.8.8/x86_64-linux-thread-multi -Darchname=x86_64-linux -Dvendorprefix=/usr -
Dsiteprefix=/usr -Duseshrplib -Dusethreads -Duseithreads -Duselargefiles -Dd_dosuid -Dd_semctl_semun -Di_db -Ui_ndbm -Di_gdbm -Di_shadow -
Di_syslog -Dman3ext=3pm -Duseperlio -Dinstallusrbinperl=n -Ubincompat5005 -Uversiononly -Dpager=/usr/bin/less -isr -Dd_gethostent_r_proto -
Ud_endhostent_r_proto -Ud_sethostent_r_proto -Ud_endprotoent_r_proto -Ud_setprotoent_r_proto -Ud_endservent_r_proto -Ud_setservent_r_proto -
Dinc_version_list=5.8.7 5.8.6 5.8.5 -Dscriptdir=/usr/bin'
     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=define use64bitall=define uselongdouble=undef
     usemymalloc=n, bincompat5005=undef
   Compiler:
     cc='gcc', ccflags ='-D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing -pipe -Wdeclaration-after-statement -I/usr/local/include -
D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64 -I/usr/include/gdbm',
     optimize='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -m64 -mtune=generic',
     cppflags='-D_REENTRANT -D_GNU_SOURCE -fno-strict-aliasing -pipe -Wdeclaration-after-statement -I/usr/local/include -I/usr/include/gdbm'
     ccversion='', gccversion='4.1.2 20070925 (Red Hat 4.1.2-33)', gccosandvers=''
     intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678
     d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16
     ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
     alignbytes=8, prototype=define
   Linker and Libraries:
     ld='gcc', ldflags =''
     libpth=/usr/local/lib64 /lib64 /usr/lib64
     libs=-lresolv -lnsl -lgdbm -ldb -ldl -lm -lcrypt -lutil -lpthread -lc
     perllibs=-lresolv -lnsl -ldl -lm -lcrypt -lutil -lpthread -lc
     libc=, so=so, useshrplib=true, libperl=libperl.so
     gnulibc_version='2.7'
   Dynamic Linking:
     dlsrc=dl_dlopen.xs, dlext=so, d_dlsymun=undef, ccdlflags='-Wl,-E -Wl,-rpath,/usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi/CORE'
     cccdlflags='-fPIC', lddlflags='-shared -O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -
m64 -mtune=generic'

Locally applied patches:



@INC for perl v5.8.8:
     /usr/lib64/perl5/site_perl/5.8.8/x86_64-linux-thread-multi
     /usr/lib64/perl5/site_perl/5.8.7/x86_64-linux-thread-multi
     /usr/lib64/perl5/site_perl/5.8.6/x86_64-linux-thread-multi
     /usr/lib64/perl5/site_perl/5.8.5/x86_64-linux-thread-multi
     /usr/lib/perl5/site_perl/5.8.8
     /usr/lib/perl5/site_perl/5.8.7
     /usr/lib/perl5/site_perl/5.8.6
     /usr/lib/perl5/site_perl/5.8.5
     /usr/lib/perl5/site_perl
     /usr/lib64/perl5/vendor_perl/5.8.8/x86_64-linux-thread-multi
     /usr/lib64/perl5/vendor_perl/5.8.7/x86_64-linux-thread-multi
     /usr/lib64/perl5/vendor_perl/5.8.6/x86_64-linux-thread-multi
     /usr/lib64/perl5/vendor_perl/5.8.5/x86_64-linux-thread-multi
     /usr/lib/perl5/vendor_perl/5.8.8
     /usr/lib/perl5/vendor_perl/5.8.7
     /usr/lib/perl5/vendor_perl/5.8.6
     /usr/lib/perl5/vendor_perl/5.8.5
     /usr/lib/perl5/vendor_perl
     /usr/lib64/perl5/5.8.8/x86_64-linux-thread-multi
     /usr/lib/perl5/5.8.8
     .


Environment for perl v5.8.8:
     HOME=/home/GMCH
     LANG=en_GB.UTF-8
     LANGUAGE (unset)
     LD_LIBRARY_PATH (unset)
     LOGDIR (unset)
     PATH=/usr/kerberos/sbin:/usr/kerberos/bin:/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin
     PERL_BADLANG (unset)
     SHELL=/bin/bash

-- 
Chris Hall               highwayman.com

@p5pRT
Copy link
Author

p5pRT commented May 7, 2008

From shouldbedomo@mac.com

On 2008–05–06, at 19​:58, Chris Hall (via RT) wrote​:

Good news -- from the point of view of repeatability​: I get the same
results as you when running your test program on a 64-bit blead perl
(patchlevel 33783).

The first section shows the effect. Where the integer division is
exact we
get the right integer result. Where it is not exact, the result is
floated,
with hopeless loss of significance !

Bad news from the point of view of understanding the phenomenon​: I get
the same results as you when I put -Minteger on the 64-bit perl
command line; that is, when arithmetic operations are carried out with
machine integers and fractional parts are truncated. Same if I put -
Mbigint, replacing all machine arithmetic with arbitrary precision
integer operations.

These modules appear to be behaving themselves​:

domo​:64-bit_perl-current$ ./perl -lwe '$x=2**60 + 3; print $x/2 - 2**59'
0
domo​:64-bit_perl-current$ ./perl -Ilib -Minteger -lwe '$x=2**60 + 3;
print $x/2 - 2**59'
1
domo​:64-bit_perl-current$ ./perl -Ilib -Mbigint -lwe '$x=2**60 + 3;
print $x/2 - 2**59'
1

Can you say why I should be getting the same results from your test
program in all three cases?
--
Dominic Dunlop

@p5pRT
Copy link
Author

p5pRT commented May 7, 2008

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

@p5pRT
Copy link
Author

p5pRT commented May 7, 2008

From chris.hall@highwayman.com

On Wed, 7 May 2008 you wrote

On 2008–05–06, at 19​:58, Chris Hall (via RT) wrote​:

Good news -- from the point of view of repeatability​: I get the same
results as you when running your test program on a 64-bit blead perl
(patchlevel 33783).

The first section shows the effect. Where the integer division is
exact we
get the right integer result. Where it is not exact, the result is
floated,
with hopeless loss of significance !

Bad news from the point of view of understanding the phenomenon​: I get
the same results as you when I put -Minteger on the 64-bit perl
command line; that is, when arithmetic operations are carried out with
machine integers and fractional parts are truncated. Same if I put -
Mbigint, replacing all machine arithmetic with arbitrary precision
integer operations.

These modules appear to be behaving themselves​:

domo​:64-bit_perl-current$ ./perl -lwe '$x=2**60 + 3; print $x/2 - 2**59'
0

Documentation says that 2**n is a floating value. So you'll find that​:

  $ perl -lwe '$x=2**63 + 27; print $x - 2**63'
  0

More interesting is​:

  $ perl -lwe '$x=(1<<62) + 27; print $x - (1<<62)'
  27

  $ perl -lwe '$x=(1<<62) + 27; print $x/2 - (1<<61)'
  0

but when the division is exact​:

  $ perl -lwe '$x=(1<<62) + 26; print $x/2 - (1<<61)'
  13

When $x = 2**62 + 27, $x/2 is 2**61 + 13.5 -- forcing that to floating
point introduces a bigger error than rounding to 64-bit integer.

domo​:64-bit_perl-current$ ./perl -Ilib -Minteger -lwe '$x=2**60 + 3;
print $x/2 - 2**59'
1

umm... this is different from the result when $x/2 is pushed into
floating form... which is what one would expect.

With -Minteger 2**n is clearly an integer​:

  $ perl -Ilib -Minteger -lwe '$x=(2**62) + 27; print $x - (2**62)'
  27

And, since division now truncates​:

  $ perl -Ilib -Minteger -lwe '$x=(2**62) + 27; print $x/2 - (2**61)'
  13

domo​:64-bit_perl-current$ ./perl -Ilib -Mbigint -lwe '$x=2**60 + 3;
print $x/2 - 2**59'
1

Similarly​:

  $ perl -Ilib -Mbigint -lwe '$x=(2**62) + 27; print $x/2 - (2**61)'
  13

Is the same as -Minteger, but different from the default case.

Can you say why I should be getting the same results from your test
program in all three cases?

Not really. If I run my test with -Minteger, I get​:

  a = 0x0FEDCBA987654321 * 9 + i, for i = 0..9
  i​: d = int(a/9) : a/9 : a%9 : a - d*9
  0​: 0x0FEDCBA987654321 : 1147797409030816545 : +0 : +0
  1​: 0x0FEDCBA987654321 : 1147797409030816545 : +1 : +1
  2​: 0x0FEDCBA987654321 : 1147797409030816545 : +2 : +2
  3​: 0x0FEDCBA987654321 : 1147797409030816545 : +3 : +3
  4​: 0x0FEDCBA987654321 : 1147797409030816545 : +4 : +4
  5​: 0x0FEDCBA987654321 : 1147797409030816545 : +5 : +5
  6​: 0x0FEDCBA987654321 : 1147797409030816545 : +6 : +6
  7​: 0x0FEDCBA987654321 : 1147797409030816545 : +7 : +7
  8​: 0x0FEDCBA987654321 : 1147797409030816545 : +8 : +8
  9​: 0x0FEDCBA987654322 : 1147797409030816546 : +0 : +0
  ______________________________________________________________________
  a = 0x0010000000000000 * 2 + i, for i = 0..3
  i​: d = int(a/2) : a/2 : a%2 : a - d*2
  0​: 0x0010000000000000 : 4503599627370496 : +0 : +0
  1​: 0x0010000000000000 : 4503599627370496 : +1 : +1
  2​: 0x0010000000000001 : 4503599627370497 : +0 : +0
  3​: 0x0010000000000001 : 4503599627370497 : +1 : +1

which is what you'd expect for truncating integer division...

...and not the same as the default case, where the division is rounded
into floating point​:

  a = 0x0FEDCBA987654321 * 9 + i, for i = 0..9
  i​: d = int(a/9) : a/9 : a%9 : a - d*9
  0​: 0x0FEDCBA987654321 : 1147797409030816545 : +0 : +0
  1​: 0x0FEDCBA987654380 : 1.14779740903082e+18 : +1 : -854 <<< !!
  2​: 0x0FEDCBA987654380 : 1.14779740903082e+18 : +2 : -853 <<< !!
  3​: 0x0FEDCBA987654380 : 1.14779740903082e+18 : +3 : -852 <<< !!
  4​: 0x0FEDCBA987654380 : 1.14779740903082e+18 : +4 : -851 <<< !!
  5​: 0x0FEDCBA987654380 : 1.14779740903082e+18 : +5 : -850 <<< !!
  6​: 0x0FEDCBA987654380 : 1.14779740903082e+18 : +6 : -849 <<< !!
  7​: 0x0FEDCBA987654380 : 1.14779740903082e+18 : +7 : -848 <<< !!
  8​: 0x0FEDCBA987654380 : 1.14779740903082e+18 : +8 : -847 <<< !!
  9​: 0x0FEDCBA987654322 : 1147797409030816546 : +0 : +0
  ______________________________________________________________________
  a = 0x0010000000000000 * 2 + i, for i = 0..3
  i​: d = int(a/2) : a/2 : a%2 : a - d*2
  0​: 0x0010000000000000 : 4503599627370496 : +0 : +0
  1​: 0x0010000000000000 : 4503599627370496 : +1 : +1
  2​: 0x0010000000000001 : 4503599627370497 : +0 : +0
  3​: 0x0010000000000002 : 4503599627370498 : +1 : -1 <<< !!

Are you getting something else ?

Chris
--
Chris Hall highwayman.com

@p5pRT
Copy link
Author

p5pRT commented Jun 8, 2016

From @dcollinsn

I have attempted to reproduce the original testcase, and 64-bit-all builds of both 5.20.0 and blead behave properly​:

a = 0x0FEDCBA987654321 * 9 + i, for i = 0..9
  i​: d = int(a/9) : a/9 : a%9 : a - d*9
  0​: 0x0FEDCBA987654321 : 1147797409030816545 : +0 : +0
  1​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +1 : +1
  2​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +2 : +2
  3​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +3 : +3
  4​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +4 : +4
  5​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +5 : +5
  6​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +6 : +6
  7​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +7 : +7
  8​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +8 : +8
  9​: 0x0FEDCBA987654322 : 1147797409030816546 : +0 : +0
______________________________________________________________________
a = 0x0010000000000000 * 2 + i, for i = 0..3
  i​: d = int(a/2) : a/2 : a%2 : a - d*2
  0​: 0x0010000000000000 : 4503599627370496 : +0 : +0
  1​: 0x0010000000000000 : 4503599627370496.5 : +1 : +1
  2​: 0x0010000000000001 : 4503599627370497 : +0 : +0
  3​: 0x0010000000000001 : 4503599627370497.5 : +1 : +1

Unless anyone has a different understanding of this issue, we should write a test case and close this bug.

@p5pRT
Copy link
Author

p5pRT commented Jun 8, 2016

From [Unknown Contact. See original ticket]

I have attempted to reproduce the original testcase, and 64-bit-all builds of both 5.20.0 and blead behave properly​:

a = 0x0FEDCBA987654321 * 9 + i, for i = 0..9
  i​: d = int(a/9) : a/9 : a%9 : a - d*9
  0​: 0x0FEDCBA987654321 : 1147797409030816545 : +0 : +0
  1​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +1 : +1
  2​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +2 : +2
  3​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +3 : +3
  4​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +4 : +4
  5​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +5 : +5
  6​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +6 : +6
  7​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +7 : +7
  8​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +8 : +8
  9​: 0x0FEDCBA987654322 : 1147797409030816546 : +0 : +0
______________________________________________________________________
a = 0x0010000000000000 * 2 + i, for i = 0..3
  i​: d = int(a/2) : a/2 : a%2 : a - d*2
  0​: 0x0010000000000000 : 4503599627370496 : +0 : +0
  1​: 0x0010000000000000 : 4503599627370496.5 : +1 : +1
  2​: 0x0010000000000001 : 4503599627370497 : +0 : +0
  3​: 0x0010000000000001 : 4503599627370497.5 : +1 : +1

Unless anyone has a different understanding of this issue, we should write a test case and close this bug.

@p5pRT
Copy link
Author

p5pRT commented Jun 8, 2016

From @dcollinsn

mea culpa - This (and #62746) still fail in blead with -Duse64bitall
-Uuselongdouble. Presumably, a similar testcase also fails under
-Duselongdouble.

On Tue, Jun 7, 2016 at 8​:08 PM, Dan Collins via RT <perlbug-comment@​perl.org

wrote​:

I have attempted to reproduce the original testcase, and 64-bit-all builds
of both 5.20.0 and blead behave properly​:

a = 0x0FEDCBA987654321 * 9 + i, for i = 0..9
i​: d = int(a/9) : a/9 : a%9 : a - d*9
0​: 0x0FEDCBA987654321 : 1147797409030816545 : +0 : +0
1​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +1 : +1
2​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +2 : +2
3​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +3 : +3
4​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +4 : +4
5​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +5 : +5
6​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +6 : +6
7​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +7 : +7
8​: 0x0FEDCBA987654321 : 1.14779740903081655e+18 : +8 : +8
9​: 0x0FEDCBA987654322 : 1147797409030816546 : +0 : +0
______________________________________________________________________
a = 0x0010000000000000 * 2 + i, for i = 0..3
i​: d = int(a/2) : a/2 : a%2 : a - d*2
0​: 0x0010000000000000 : 4503599627370496 : +0 : +0
1​: 0x0010000000000000 : 4503599627370496.5 : +1 : +1
2​: 0x0010000000000001 : 4503599627370497 : +0 : +0
3​: 0x0010000000000001 : 4503599627370497.5 : +1 : +1

Unless anyone has a different understanding of this issue, we should write
a test case and close this bug.

@danaj
Copy link

danaj commented Feb 21, 2021

Long double means different things on different platforms. On many (e.g. Mac M1) it is equal to double, so we will see the same behavior. On some x86-64 hardware + compilers, it is extended. On others, or with quadmath, it is greatly extended.

'use integer' has other issues:

  • It treats everything as an IV. This means unsigned numbers greater than 2^63 will get mangled. Two unsigned integers can give a negative quotient.
  • Negative inputs are implementation defined in C89. C99 fixed this (thank goodness). Hence any truly portable and consistent solution is going to have to go through gyrations to ensure consistent results.
perl -E 'say int(9223372036854785808/14); { use integer; say 9223372036854785808/14; }'
658812288346770432
-658812288346768986

Neither of which are correct. The first because the division is done as FP then converted back to an integer with loss of precision. The second because 'use integer' cast the first number into an IV.

On an x86-64 with long double (nvsize=16), the first result does turn out correct, as does a quadmath build (__float128). But the same machine without long double or quadmath gives the incorrect result. My M1 Mac, built with long double, also gives the incorrect result, as expected since that hardware/compiler treats long double = double.

I'm not sure if this is directly applicable to the OP's issue, but maybe sheds some light on the tests.

A solution for correct integer division is either using a module or adding a new operator for correct 64-bit integer floored division (e.g. Raku's div and mod, or Pari/GP's '', or Python's '//'). This would also have the benefit of being more consistent with the existing floored mod operator. This seems highly unlikely to happen.

@sisyphus
Copy link
Contributor

Seems that perl resorts to FP division of integers only if the exact result of the division is not an integer.
So, I'm thinking a sub divmod could be written simply as:

sub divmod {
  $mod = $_[0] % $_[1];
  return (($_[0] - $mod) / $_[1], $mod)}
}

Admittedly, that's not as convenient as having actual operators that will return the desired results but, apart from that, is there any deficiency with it ?
BTW, divmod (9223372036854785808, 14) returns (658812288346770414, 12) for me on perl-5.32.0.

@danaj
Copy link

danaj commented Mar 2, 2021

Seems that perl resorts to FP division of integers only if the exact result of the division is not an integer.

I wish that was true, but there are exceptions. E.g. Perl 5.32.1 and 5.33.7 give me:

    $ perl -E 'say 1125899906842624/1'
    1.12589990684262e+15

    $ perl -E 'say 2232881419280026/2'
    1.11644070964001e+15

Argh. If it worked, we could make your divmod function give the truncated quotient and remainder, like C99, for both positive and negative numbers, using one (ugly) extra line of code, e.g.:

sub tdivrem {
  my($D,$d) = @_;
  my $mod = $D % $d;
  $mod -= $d if $mod != 0 && (($D < 0 && $d >= 0) || ($D >= 0 && $d < 0));
  return ($D - $mod) / $d;  # Sadly this doesn't work for all inputs
}

It works for most inputs, but there is a range that results in bad results.

@sisyphus
Copy link
Contributor

sisyphus commented Mar 2, 2021

Yes - I misread the behaviour, and on a perl whose nvsize is 8 it's not going to work if the division results in a value that cannot be represented exactly in 53 (or fewer) bits.
It should, however, be fine on a perl whose nvsize is greater than 8.

For perls whose nvsize and ivsize are both 8, I think you'll have to resort to XS or Inline::C. Math::Int64 might already provide just what's needed, but I haven't checked on that.
Using one of the various big integer modules could also be an (more expensive) option - though I don't know if they'll provide the results you want when negative values are involved.

@xenu xenu removed the Severity Low label Dec 29, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants