Reputation:
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
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
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
:
$b
is positive, then $a % $b
is $a
minus the largest multiple of $b
less than or equal to $a
. $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). $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). 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