user3120639
user3120639

Reputation:

Perl: understanding modulo operation on negative numbers (e.g. -10%3)

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?

Upvotes: 4

Views: 3710

Answers (2)

rurban
rurban

Reputation: 4131

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.

Upvotes: 6

Bura Chuhadar
Bura Chuhadar

Reputation: 3751

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.

Upvotes: 3

Related Questions