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

Possible bug in remainder computation (m % n) for negative numbers -- #17097

Closed
p5pRT opened this issue Jul 16, 2019 · 12 comments
Closed

Possible bug in remainder computation (m % n) for negative numbers -- #17097

p5pRT opened this issue Jul 16, 2019 · 12 comments

Comments

@p5pRT
Copy link

p5pRT commented Jul 16, 2019

Migrated from rt.perl.org#134290 (status was 'rejected')

Searchable as RT134290$

@p5pRT
Copy link
Author

p5pRT commented Jul 16, 2019

From aab@purdue.edu

The attachment is the 'perlbug' output.

-- Thanks,

-- Paul Townsend

@p5pRT
Copy link
Author

p5pRT commented Jul 16, 2019

From aab@purdue.edu

Created by aab@purdue.edu

Without 'use integer;', I think 'perl' miscomputes the remainder op (m % n) for
negative numbers. Below is a script and results that demonstrate the problem.

cat tst6
print "\n'perl' without 'use integer;'\n";
print "11 % 2 = ", 11 % 2, "\n";
print "-11 % 2 = ", -11 % 2, " <- should be -1\n";

use integer;

print "\n'perl' with 'use integer;'\n";
print "11 % 2 = ", 11 % 2, "\n";
print "-11 % 2 = ", -11 % 2, "\n";

print "\n'perl' using straight computation\n";
print "(11 - int(11 / 2) * 2) = ", (11 - int(11 / 2) * 2), "\n";
print "(-11 - int(-11 / 2) * 2) = ", (-11 - int(-11 / 2) * 2), "\n";

perl tst6

'perl' without 'use integer;'
11 % 2 = 1
-11 % 2 = 1 <- should be -1

'perl' with 'use integer;'
11 % 2 = 1
-11 % 2 = -1

'perl' using straight computation
(11 - int(11 / 2) * 2) = 1
(-11 - int(-11 / 2) * 2) = -1

I started using 'perl4' in the good-ol-days and have enjoyed using it ever
since. But I think that this is the first time that I have ever encountered a
simple(?) bug and I just had to wait until after I retired to find it (​:-}).

Thanks for all your hard work.

  Paul Townsend (aab@​purdue.edu)

Perl Info

Flags:
    category=core
    severity=medium

Site configuration information for perl 5.26.3:

Configured by ASSI at Sat Dec  1 08:14:42 CET 2018.

Summary of my perl5 (revision 5 version 26 subversion 3) configuration:
   
  Platform:
    osname=cygwin
    osvers=2.11.2(0.32953)
    archname=x86_64-cygwin-threads-multi
    uname='cygwin_nt-6.3 cygwin 2.11.2(0.32953) 2018-11-08 14:34 x86_64 cygwin '
    config_args='-des -Dprefix=/usr -Dmksymlinks -Darchname=x86_64-cygwin-threads -Dlibperl=cygperl5_26.dll -Dcc=gcc -Dld=g++ -Accflags=-ggdb -O2 -pipe -Wall -Werror=format-security -D_FORTIFY_SOURCE=2 -fstack-protector-strong --param=ssp-buffer-size=4 -fdebug-prefix-map=/mnt/share/maint/perl.x86_64/build=/usr/src/debug/perl-5.26.3-1 -fdebug-prefix-map=/mnt/share/maint/perl.x86_64/src/perl-5.26.3=/usr/src/debug/perl-5.26.3-1 -fwrapv'
    hint=recommended
    useposix=true
    d_sigaction=define
    useithreads=define
    usemultiplicity=define
    use64bitint=define
    use64bitall=define
    uselongdouble=undef
    usemymalloc=n
    default_inc_excludes_dot=define
    bincompat5005=undef
  Compiler:
    cc='gcc'
    ccflags ='-DPERL_USE_SAFE_PUTENV -U__STRICT_ANSI__ -D_GNU_SOURCE -ggdb -O2 -pipe -Wall -Werror=format-security -D_FORTIFY_SOURCE=2 -fstack-protector-strong --param=ssp-buffer-size=4 -fdebug-prefix-map=/mnt/share/maint/perl.x86_64/build=/usr/src/debug/perl-5.26.3-1 -fdebug-prefix-map=/mnt/share/maint/perl.x86_64/src/perl-5.26.3=/usr/src/debug/perl-5.26.3-1 -fwrapv -fno-strict-aliasing'
    optimize='-O3'
    cppflags='-DPERL_USE_SAFE_PUTENV -U__STRICT_ANSI__ -D_GNU_SOURCE -ggdb -O2 -pipe -Wall -Werror=format-security -D_FORTIFY_SOURCE=2 -fstack-protector-strong --param=ssp-buffer-size=4 -fdebug-prefix-map=/mnt/share/maint/perl.x86_64/build=/usr/src/debug/perl-5.26.3-1 -fdebug-prefix-map=/mnt/share/maint/perl.x86_64/src/perl-5.26.3=/usr/src/debug/perl-5.26.3-1 -fwrapv -fno-strict-aliasing'
    ccversion=''
    gccversion='7.3.0'
    gccosandvers=''
    intsize=4
    longsize=8
    ptrsize=8
    doublesize=8
    byteorder=12345678
    doublekind=3
    d_longlong=define
    longlongsize=8
    d_longdbl=define
    longdblsize=16
    longdblkind=3
    ivtype='long'
    ivsize=8
    nvtype='double'
    nvsize=8
    Off_t='off_t'
    lseeksize=8
    alignbytes=8
    prototype=define
  Linker and Libraries:
    ld='g++'
    ldflags =' -Wl,--enable-auto-import -Wl,--export-all-symbols -Wl,--enable-auto-image-base -fstack-protector-strong'
    libpth=/usr/lib
    libs=-lpthread -lgdbm -ldb -ldl -lcrypt -lgdbm_compat
    perllibs=-lpthread -ldl -lcrypt
    libc=/usr/lib/libcygwin.a
    so=dll
    useshrplib=true
    libperl=cygperl5_26.dll
    gnulibc_version=''
  Dynamic Linking:
    dlsrc=dl_dlopen.xs
    dlext=dll
    d_dlsymun=undef
    ccdlflags=' '
    cccdlflags=' '
    lddlflags=' --shared  -Wl,--enable-auto-import -Wl,--export-all-symbols -Wl,--enable-auto-image-base -fstack-protector-strong'

Locally applied patches:
    Cygwin: README
    Cygwin: use auto-image-base instead of fixed DLL base address
    Cygwin: modify hints
    Cygwin: Configure correct libsearch
    Cygwin: Configure correct libpth
    Cygwin: Win32 correct UTF8 handling
    Perl: File-Path-2.14 (fixes CVE2017-6512)


@INC for perl 5.26.3:
    /usr/local/lib/perl5/site_perl/5.26/x86_64-cygwin-threads
    /usr/local/share/perl5/site_perl/5.26
    /usr/lib/perl5/vendor_perl/5.26/x86_64-cygwin-threads
    /usr/share/perl5/vendor_perl/5.26
    /usr/lib/perl5/5.26/x86_64-cygwin-threads
    /usr/share/perl5/5.26


Environment for perl 5.26.3:
    CYGWIN=winsymlinks:nativestrict
    HOME=/home/aab
    LANG=en_US.UTF-8
    LANGUAGE (unset)
    LD_LIBRARY_PATH (unset)
    LOGDIR (unset)
    PATH=.:/home/aab/bin:/usr/local/bin:/usr/bin:/cygdrive/c/WINDOWS/system32:/cygdrive/c/WINDOWS:/cygdrive/c/WINDOWS/System32/Wbem:/cygdrive/c/WINDOWS/System32/WindowsPowerShell/v1.0:/cygdrive/c/WINDOWS/System32/OpenSSH:/cygdrive/c/Program Files (x86)/Intel/Intel(R) Management Engine Components/DAL:/cygdrive/c/Program Files/Intel/Intel(R) Management Engine Components/DAL:/cygdrive/c/Program Files (x86)/NVIDIA Corporation/PhysX/Common:/cygdrive/c/Program Files/dotnet:/cygdrive/c/Program Files (x86)/NTP/bin:/cygdrive/c/Program Files/Intel/WiFi/bin:/cygdrive/c/Program Files/Common Files/Intel/WirelessCommon:/cygdrive/c/Users/aab/AppData/Local/Microsoft/WindowsApps:/cygdrive/c/Program Files (x86)/Nmap
    PERL_BADLANG (unset)
    SHELL=/bin/bash

@p5pRT
Copy link
Author

p5pRT commented Jul 17, 2019

From @xenu

(posting again, this time via RT web interface, because it seems that the mailing list integration has swallowed my reply)

On Wed, 17 Jul 2019, at 01​:38, Paul Townsend (via RT) wrote​:

# New Ticket Created by Paul Townsend
# Please include the string​: [perl #134290]
# in the subject line of all future correspondence about this issue.
# <URL​: https://rt-archive.perl.org/perl5/Ticket/Display.html?id=134290 >

The attachment is the 'perlbug' output.

-- Thanks,

-- Paul Townsend

Attachments​:
* perlbug.txt

The operator behaves as documented, see https://perldoc.pl/perlop#Multiplicative-Operators.

Given integer operands $m and $n : If $n is positive, then $m % $n
is $m minus the largest multiple of $n less than or equal to $m .

In case of -11 % 2, the largest multiple of 2 that is less than or
equal to -11 is -12.

-11 - (-12) = 1

According to perldoc, with 'use integer' enabled, the behaviour of
modulo operator with negative operands is undefined​:

Note that when use integer is in scope, "%" gives you direct access
to the modulo operator as implemented by your C compiler. This
operator is not as well defined for negative operands, but it will
execute faster.

PS. As you can see on Wikipedia[1] there's no consensus on how modulo
operator should work with negative operands, so it differs from
language to language

[1] - https://en.wikipedia.org/wiki/Modulo_operation#Remainder_calculation_for_the_modulo_operation

@p5pRT
Copy link
Author

p5pRT commented Jul 17, 2019

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

@p5pRT
Copy link
Author

p5pRT commented Jul 17, 2019

From @sisyphus

Just wanted to add that python3, python2, and perl6 all treat -ve operands
in the same way as perl.
Seems that the integer pragma (and hence also C) are the odd ones out, in
that group at least.

The GMP library ignores the sign of the divisor, but not the sign of the
dividend, and always returns a +ve result.
That's the implementation that DWIMs for me.
I think (not properly tested) it's essentially the same as perl, except
perl can return negative values.
For example​:
In perl 4 % -3 == -2
The GMP library yields 4 % -3 == 1

They're the same result because -2 modulo 3 is congruent to 1 modulo 3.

If one wants to portably allow -ve operands in mod operations, it might be
best to use a function that does exactly what's intended, rather than
relying upon the % operator.

Cheers,
Rob

On Wed, Jul 17, 2019 at 10​:37 AM Tomasz Konojacki via RT <
perlbug-followup@​perl.org> wrote​:

(posting again, this time via RT web interface, because it seems that the
mailing list integration has swallowed my reply)

On Wed, 17 Jul 2019, at 01​:38, Paul Townsend (via RT) wrote​:

# New Ticket Created by Paul Townsend
# Please include the string​: [perl #134290]
# in the subject line of all future correspondence about this issue.
# <URL​: https://rt-archive.perl.org/perl5/Ticket/Display.html?id=134290 >

The attachment is the 'perlbug' output.

-- Thanks,

-- Paul Townsend

Attachments​:
* perlbug.txt

The operator behaves as documented, see
https://perldoc.pl/perlop#Multiplicative-Operators.

Given integer operands $m and $n : If $n is positive, then $m % $n
is $m minus the largest multiple of $n less than or equal to $m .

In case of -11 % 2, the largest multiple of 2 that is less than or
equal to -11 is -12.

-11 - (-12) = 1

According to perldoc, with 'use integer' enabled, the behaviour of
modulo operator with negative operands is undefined​:

Note that when use integer is in scope, "%" gives you direct access
to the modulo operator as implemented by your C compiler. This
operator is not as well defined for negative operands, but it will
execute faster.

PS. As you can see on Wikipedia[1] there's no consensus on how modulo
operator should work with negative operands, so it differs from
language to language

[1] -
https://en.wikipedia.org/wiki/Modulo_operation#Remainder_calculation_for_the_modulo_operation

---
via perlbug​: queue​: perl5 status​: new
https://rt-archive.perl.org/perl5/Ticket/Display.html?id=134290

@p5pRT
Copy link
Author

p5pRT commented Jul 17, 2019

From aab@purdue.edu

This is from an old fogey who learned his math way back when. Back in the day, a remainder for the division 'a / b' was computed as

  r = a - (trunc (a /b) * b)

Unless r = 0, the sign of 'r' is always the same as the sign of 'a'. The sign of 'b' doesn't matter since it's "canceled" because 'b' is effectively in both the numerator and denominator of '(trunc (a /b) * b)'.

Now IEEE comes along and throws in a rounding consideration for the 'a / b'. This is NOT the '%' remainder! If folks wish to do it IEEE's way, a separate function should be provided for it. I have noted functions on the net named IEEEremainder and std​::remainder that do the job. In perl, the equation seems to be

  r = a - (round(a/b) * b)

I think that computing a remainder any way other than using the first equation above can produce an incorrect result.

Okay, enough ranting.

-- Thanks,

-- Paul Townsend

________________________________
From​: sisyphus via RT <perlbug-followup@​perl.org>
Sent​: Tuesday, July 16, 2019 9​:30 PM
To​: Townsend, Paul
Subject​: Re​: [perl #134290] Possible bug in remainder computation (m % n) for negative numbers --

Just wanted to add that python3, python2, and perl6 all treat -ve operands
in the same way as perl.
Seems that the integer pragma (and hence also C) are the odd ones out, in
that group at least.

The GMP library ignores the sign of the divisor, but not the sign of the
dividend, and always returns a +ve result.
That's the implementation that DWIMs for me.
I think (not properly tested) it's essentially the same as perl, except
perl can return negative values.
For example​:
In perl 4 % -3 == -2
The GMP library yields 4 % -3 == 1

They're the same result because -2 modulo 3 is congruent to 1 modulo 3.

If one wants to portably allow -ve operands in mod operations, it might be
best to use a function that does exactly what's intended, rather than
relying upon the % operator.

Cheers,
Rob

On Wed, Jul 17, 2019 at 10​:37 AM Tomasz Konojacki via RT <
perlbug-followup@​perl.org> wrote​:

(posting again, this time via RT web interface, because it seems that the
mailing list integration has swallowed my reply)

On Wed, 17 Jul 2019, at 01​:38, Paul Townsend (via RT) wrote​:

# New Ticket Created by Paul Townsend
# Please include the string​: [perl #134290]
# in the subject line of all future correspondence about this issue.
# <URL​: https://rt-archive.perl.org/perl5/Ticket/Display.html?id=134290 >

The attachment is the 'perlbug' output.

-- Thanks,

-- Paul Townsend

Attachments​:
* perlbug.txt

The operator behaves as documented, see
https://perldoc.pl/perlop#Multiplicative-Operators.

Given integer operands $m and $n : If $n is positive, then $m % $n
is $m minus the largest multiple of $n less than or equal to $m .

In case of -11 % 2, the largest multiple of 2 that is less than or
equal to -11 is -12.

-11 - (-12) = 1

According to perldoc, with 'use integer' enabled, the behaviour of
modulo operator with negative operands is undefined​:

Note that when use integer is in scope, "%" gives you direct access
to the modulo operator as implemented by your C compiler. This
operator is not as well defined for negative operands, but it will
execute faster.

PS. As you can see on Wikipedia[1] there's no consensus on how modulo
operator should work with negative operands, so it differs from
language to language

[1] -
https://en.wikipedia.org/wiki/Modulo_operation#Remainder_calculation_for_the_modulo_operation

---
via perlbug​: queue​: perl5 status​: new
https://rt-archive.perl.org/perl5/Ticket/Display.html?id=134290

@p5pRT
Copy link
Author

p5pRT commented Jul 18, 2019

From @sisyphus

On Wed, 17 Jul 2019 10​:59​:56 -0700, aab@​purdue.edu wrote​:

This is from an old fogey who learned his math way back when. Back in
the day, a remainder for the division 'a / b' was computed as

r = a - (trunc (a /b) * b)

[snip]

I think that computing a remainder any way other than using the first
equation above can produce an incorrect result.

Yep - I've no issue with any of that.
And that produces exactly the same results as the % operator if the operands are +ve.
If that formula is doing what you want, then keep using that formula instead of the % operator if -ve operands can be present.

Regarding the % operator as a "modulo arithmetic" operator it's just as valid to say that -11 % 2 is 1, as it is to say -11 % 2 is -1.
That's because -1 modulo 2 = +1 modulo 2.
Similarly -5 modulo 7 = +2 modulo 7.
In general, the condition is that -x modulo y = y-x modulo y.
(The "=" signs should be read as "is congruent to".)

I personally think that the positive value is the one that should be returned in all modulo operations but, as we've seen, that's not always the case.

However, the value returned by ($x % $y) should always be congruent to the value returned by your remainder formula % $y.

Note that the following script detects no inconsistency for your formula versus the % operator - and that's for both -ve and +ve dividends, and irrespective of whether the integer pragma is in use.

################################
use strict;
use warnings;
use integer;

# With +ve dividends
for my $d (1..1000) {
  for my $mod (1..1000) {
  print "WTF ?\n"
  if $d % $mod != rem($d, $mod);
  }
}

# With -ve dividends
for my $d (1..1000) {
  $d *= -1;
  for my $mod (1..1000) {
  print "WTF ?\n"
  unless is_congruent($d, $mod);
  }
}

# Check whether $d % $mod is congruent to rem($d, $mod)
sub is_congruent {
  my($d, $mod) = (shift, shift);
  my $r1 = $d % $mod;
  my $r2 = rem($d, $mod);
  return 1 if $r1 == $r2;
  return 1 if min($r1, $r2) + $mod == max($r1, $r2);
  return 0;
}

# Calculate remainder
sub rem {
  my($d, $mod) = (shift, shift);
  return $d - int($d / $mod) * $mod;
}

sub min {
  return $_[0] if $_[0] < $_[1];
  return $_[1];
}

sub max {
  return $_[0] if $_[0] > $_[1];
  return $_[1];
}

########################################

Cheers,
Rob

@p5pRT
Copy link
Author

p5pRT commented Jul 24, 2019

From aab@purdue.edu

I think that there is an identity here that is occasionally being violated for at least some negative numbers when using 'round' in the remainder calculation. It is always true for "trunc". a = -11, b = 2 is a counter example for "round".

  s = a < 0 ? -1 : 1

  a % b == s * (abs (a) % b)

I think that we tend to forget that machine floating point numbers are, at worst, approximations only. Whether the division result is rounded up or down can at times be in the lap of the gods.

A good example of the effects of approximation is found in the 'int' description in the perlfunc(1) man page. It uses -6.725/0.025 as the example. If you are just eyeballing it, the result should be -269. However, the actual floating point result on a 64-bit box is -268.9999999999999432. 'round' works nicely here.

-- Thanks and have a great day,

-- Paul Townsend

________________________________
From​: sisyphus@​cpan.org via RT <perlbug-followup@​perl.org>
Sent​: Thursday, July 18, 2019 12​:15 AM
To​: Townsend, Paul <aab@​purdue.edu>
Subject​: [perl #134290] Possible bug in remainder computation (m % n) for negative numbers --

On Wed, 17 Jul 2019 10​:59​:56 -0700, aab@​purdue.edu wrote​:

This is from an old fogey who learned his math way back when. Back in
the day, a remainder for the division 'a / b' was computed as

r = a - (trunc (a /b) * b)

[snip]

I think that computing a remainder any way other than using the first
equation above can produce an incorrect result.

Yep - I've no issue with any of that.
And that produces exactly the same results as the % operator if the operands are +ve.
If that formula is doing what you want, then keep using that formula instead of the % operator if -ve operands can be present.

Regarding the % operator as a "modulo arithmetic" operator it's just as valid to say that -11 % 2 is 1, as it is to say -11 % 2 is -1.
That's because -1 modulo 2 = +1 modulo 2.
Similarly -5 modulo 7 = +2 modulo 7.
In general, the condition is that -x modulo y = y-x modulo y.
(The "=" signs should be read as "is congruent to".)

I personally think that the positive value is the one that should be returned in all modulo operations but, as we've seen, that's not always the case.

However, the value returned by ($x % $y) should always be congruent to the value returned by your remainder formula % $y.

Note that the following script detects no inconsistency for your formula versus the % operator - and that's for both -ve and +ve dividends, and irrespective of whether the integer pragma is in use.

################################
use strict;
use warnings;
use integer;

# With +ve dividends
for my $d (1..1000) {
  for my $mod (1..1000) {
  print "WTF ?\n"
  if $d % $mod != rem($d, $mod);
  }
}

# With -ve dividends
for my $d (1..1000) {
  $d *= -1;
  for my $mod (1..1000) {
  print "WTF ?\n"
  unless is_congruent($d, $mod);
  }
}

# Check whether $d % $mod is congruent to rem($d, $mod)
sub is_congruent {
  my($d, $mod) = (shift, shift);
  my $r1 = $d % $mod;
  my $r2 = rem($d, $mod);
  return 1 if $r1 == $r2;
  return 1 if min($r1, $r2) + $mod == max($r1, $r2);
  return 0;
}

# Calculate remainder
sub rem {
  my($d, $mod) = (shift, shift);
  return $d - int($d / $mod) * $mod;
}

sub min {
  return $_[0] if $_[0] < $_[1];
  return $_[1];
}

sub max {
  return $_[0] if $_[0] > $_[1];
  return $_[1];
}

########################################

Cheers,
Rob

@p5pRT
Copy link
Author

p5pRT commented Aug 1, 2019

From @tonycoz

On Wed, 17 Jul 2019 10​:59​:56 -0700, aab@​purdue.edu wrote​:

This is from an old fogey who learned his math way back when. Back in
the day, a remainder for the division 'a / b' was computed as

r = a - (trunc (a /b) * b)

Unless r = 0, the sign of 'r' is always the same as the sign of 'a'.
The sign of 'b' doesn't matter since it's "canceled" because 'b' is
effectively in both the numerator and denominator of '(trunc (a /b) *
b)'.

Now IEEE comes along and throws in a rounding consideration for the 'a
/ b'. This is NOT the '%' remainder! If folks wish to do it IEEE's
way, a separate function should be provided for it. I have noted
functions on the net named IEEEremainder and std​::remainder that do
the job. In perl, the equation seems to be

r = a - (round(a/b) * b)

I think that computing a remainder any way other than using the first
equation above can produce an incorrect result.

The code that calculates the remainder in perl for integers is​:

  UV ans;

  if (!right)
  DIE(aTHX_ "Illegal modulus zero");

  ans = left % right;
  if ((left_neg != right_neg) && ans)
  ans = right - ans;
  if (right_neg) {
  /* XXX may warn​: unary minus operator applied to unsigned type */
  /* could change -foo to be (~foo)+1 instead */
  if (ans <= ~((UV)IV_MAX)+1)
  sv_setiv(TARG, ~ans+1);
  else
  sv_setnv(TARG, -(NV)ans);
  }
  else
  sv_setuv(TARG, ans);

Note that the actual remainder operation is done with positive numbers and then adjusted based on the original signs of the operands. There's no rounding involved.

Given this behaves as documented (or not documented with use integer), I'll close this in a day or two.

Tony

@p5pRT
Copy link
Author

p5pRT commented Aug 2, 2019

From aab@purdue.edu

I assume we are talking arithmetic using the machines integer instructions.

Whatever instruction set is used, the perl encoding of remainder is wrong. Once ans has been computed, its absolute value CANNOT change except maybe for some end cases. Also, the sign of the right hand operand will (not should) have no effect on the sign of ans which, except for zero, must have the sign of the left operand.

________________________________
From​: Tony Cook via RT <perlbug-followup@​perl.org>
Sent​: Wednesday, July 31, 2019 9​:21​:21 PM
To​: Townsend, Paul <aab@​purdue.edu>
Subject​: [perl #134290] Possible bug in remainder computation (m % n) for negative numbers --

On Wed, 17 Jul 2019 10​:59​:56 -0700, aab@​purdue.edu wrote​:

This is from an old fogey who learned his math way back when. Back in
the day, a remainder for the division 'a / b' was computed as

r = a - (trunc (a /b) * b)

Unless r = 0, the sign of 'r' is always the same as the sign of 'a'.
The sign of 'b' doesn't matter since it's "canceled" because 'b' is
effectively in both the numerator and denominator of '(trunc (a /b) *
b)'.

Now IEEE comes along and throws in a rounding consideration for the 'a
/ b'. This is NOT the '%' remainder! If folks wish to do it IEEE's
way, a separate function should be provided for it. I have noted
functions on the net named IEEEremainder and std​::remainder that do
the job. In perl, the equation seems to be

r = a - (round(a/b) * b)

I think that computing a remainder any way other than using the first
equation above can produce an incorrect result.

The code that calculates the remainder in perl for integers is​:

  UV ans;

  if (!right)
  DIE(aTHX_ "Illegal modulus zero");

  ans = left % right;
  if ((left_neg != right_neg) && ans)
  ans = right - ans;
  if (right_neg) {
  /* XXX may warn​: unary minus operator applied to unsigned type */
  /* could change -foo to be (~foo)+1 instead */
  if (ans <= ~((UV)IV_MAX)+1)
  sv_setiv(TARG, ~ans+1);
  else
  sv_setnv(TARG, -(NV)ans);
  }
  else
  sv_setuv(TARG, ans);

Note that the actual remainder operation is done with positive numbers and then adjusted based on the original signs of the operands. There's no rounding involved.

Given this behaves as documented (or not documented with use integer), I'll close this in a day or two.

Tony

@p5pRT
Copy link
Author

p5pRT commented Aug 5, 2019

From @tonycoz

On Fri, 02 Aug 2019 14​:31​:44 -0700, aab@​purdue.edu wrote​:

I assume we are talking arithmetic using the machines integer
instructions.

Whatever instruction set is used, the perl encoding of remainder is
wrong. Once ans has been computed, its absolute value CANNOT change
except maybe for some end cases. Also, the sign of the right hand
operand will (not should) have no effect on the sign of ans which,
except for zero, must have the sign of the left operand.

My point was that no rounding is done.

As Tomas pointed out the sign of the modulus/remainder operation varies from language to language, and perl's behaviour is documented, and similar to many other languages.

Changing it at this point would break existing code for no good reason.

Closing.

Tony

________________________________
From​: Tony Cook via RT <perlbug-followup@​perl.org>
Sent​: Wednesday, July 31, 2019 9​:21​:21 PM
To​: Townsend, Paul <aab@​purdue.edu>
Subject​: [perl #134290] Possible bug in remainder computation (m % n)
for negative numbers --

On Wed, 17 Jul 2019 10​:59​:56 -0700, aab@​purdue.edu wrote​:

Now IEEE comes along and throws in a rounding consideration for the
'a
/ b'. This is NOT the '%' remainder! If folks wish to do it IEEE's
way, a separate function should be provided for it. I have noted
functions on the net named IEEEremainder and std​::remainder that do
the job. In perl, the equation seems to be

r = a - (round(a/b) * b)

I think that computing a remainder any way other than using the first
equation above can produce an incorrect result.

The code that calculates the remainder in perl for integers is​:

UV ans;

if (!right)
DIE(aTHX_ "Illegal modulus zero");

ans = left % right;
if ((left_neg != right_neg) && ans)
ans = right - ans;
if (right_neg) {
/* XXX may warn​: unary minus operator applied to unsigned type */
/* could change -foo to be (~foo)+1 instead */
if (ans <= ~((UV)IV_MAX)+1)
sv_setiv(TARG, ~ans+1);
else
sv_setnv(TARG, -(NV)ans);
}
else
sv_setuv(TARG, ans);

Note that the actual remainder operation is done with positive numbers
and then adjusted based on the original signs of the operands.
There's no rounding involved.

Given this behaves as documented (or not documented with use integer),
I'll close this in a day or two.

Tony

@p5pRT
Copy link
Author

p5pRT commented Aug 5, 2019

@tonycoz - Status changed from 'open' to 'rejected'

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

1 participant