4

I'm learning Perl (5.14) and I'm a bit stuck on modulo with negative numbers. As an example, let's take a look at variations on 10%3.

To begin,

perl -le 'print -10%-3'

yields -1, as expected.

But,

perl -le 'print -10%3'

yields 2.

And,

perl -le 'print 10%-3'

yields -2.

I do not understand the last two results. I would expect only 1 or -1 as a result for any variation on 10%3. Why should 2, either positive or negative, be returned as a result?

  • See also: [Why is modulus different in different programming languages?](http://stackoverflow.com/questions/450410/why-is-modulus-different-in-different-programming-languages) and [Is the behavior of % with negative operands defined in Perl 5?](http://stackoverflow.com/questions/3610687/is-the-behavior-of-with-negative-operands-defined-in-perl-5) – Ilmari Karonen Dec 19 '13 at 22:21

2 Answers2

6

You found a perl5 specification bug/feature that will very likely never be fixed. This modulo vs i_modulo bug is even documented as such, with a weird definition of modulo, which deviates from the mathematical definition and the implementation in libc, the Standard C library.

The documentation in http://perldoc.perl.org/perlop.html#Multiplicative-Operators describes only one case, not the second. And forgets to tell the whole story.

"If $b is negative, then $a % $b is $a minus the smallest multiple of $b
that is not less than $a (that is, the result will be less than or
equal to zero)."

Thus -13 % 4 is unspecified, 13 % -4 is described as returning -3, not 1. In reality -13 % 4 returns 3 not -1.

This perl5 behavior is only weird without use integer. With use integer you get proper and fast libc behavior.

   use integer;
   print -13 % 4;  # => -1
   print  13 % -4; # => 1
   print -13 % -4; # => -1 (same with or without use integer)
   print  13 % 4;  # => 1 (same with or without use integer)

   { 
     no integer;
     print -13 % 4;  # => 3 (different to libc)
     print  13 % -4; # => -3 (different to libc)
     print -13 % -4; # => -1 (same with or without use integer)
     print  13 % 4;  # => 1 (same with or without use integer)
   }

Note that with both arguments being literal integer constants, the result is constant folded at compile-time. But even with both arguments clearly being integer types, the constant folder uses the generic modulo operator, not the specific i_modulo operator, which is used under use integer. Or with a typed perl extension with both args being integers at compile-time.

This bug was even promoted to perl6, defined in parrot and moar as in perl5. I'm not sure if the jvm backend also uses a hack to use the weird perl5 definition.

rurban
  • 4,025
  • 24
  • 27
  • Just want to remark that `-13 % 4` is not unspecified at all. In perlop(1) (I checked version 5.26.1) you can find "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`." The largest multiple of 4 that is less than or equal to -13 is value -16, thus '-13 - (-16) = 3'. – lemzwerg Aug 24 '23 at 09:41
3

Perl usually uses arithmetic modulo operator that is machine-independent.

This is taken from the Perl Documentation: Multiplicative Operators

Binary % is the modulo operator, which computes the division remainder of its first argument with respect to its second argument.

Given integer operands $a and $b:

  • If $b is positive, then $a % $b is $a minus the largest multiple of $b less than or equal to $a.
  • If $b is negative, then $a % $b is $a minus the smallest multiple of $b that is not less than $a (that is, the result will be less than or equal to zero).
  • If the operands $a and $b are floating point values and the absolute value of $b (that is abs($b)) is less than (UV_MAX + 1), only the integer portion of $a and $b will be used in the operation (Note: here UV_MAX means the maximum of the unsigned integer type).
  • If the absolute value of the right operand (abs($b)) is greater than or equal to (UV_MAX + 1), % computes the floating-point remainder $r in the equation ($r = $a - $i*$b) where $i is a certain integer that makes $r have the same sign as the right operand $b (not as the left operand $a like C function fmod()) and the absolute value less than that of $b.

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.

OGHaza
  • 4,795
  • 7
  • 23
  • 29
Bura Chuhadar
  • 3,653
  • 1
  • 14
  • 17
  • That is an unintuitive definition to me, but it does explain the observed behaviour. I wonder if many other languages define modulo in the same way. –  Dec 19 '13 at 21:54
  • Arguing that the libc implementation for one negative integer argument is "not well defined" is hilarious. libc defines both states with negative integer arguments. negative % positive and positive % negative, and is consistent with its results. The sign affects only the sign, not the number. This is machine independent. While perl5 defines only the second case, not the first one, and changes the results to deviate from libc and its own use integer case. – rurban Aug 19 '15 at 08:19
  • You're both right… In C89, which Perl5 tries to still support, both **division** and modulo with negative arguments is implementation dependent. libc provides the consistent truncating div function (does use integer use this?). C99 fixed this (thank goodness) and is well defined as truncating division/modulo for the standard operators. Perl5 seems to return the floor modulo, but int(a/b) is truncating, making it inconsistent for most users. Raku (Perl6) uses floor division for div and mod, which is consistent. – DanaJ Feb 11 '21 at 12:05