The basic premise is that although the symbol is the same, the meaning may be different.
This difference occurs among several other programming languages as well. There are languages that choose names like rem
and mod
, which removes the ambiguity brought by %
.
There are various ways to define the "Operation Module" on computers. The difference arises when computing the quotient. The two most common ones are:
mod(a, b) = a - b * floor(a / b)
rem(a, b) = a - b * trunc(a / b)
Realize that the mod
round the result of the division down. O rem
, however, round the number to zero. See the difference between negative and positive numbers in 1.5
and -1.5
:
floor(1.5) = 1, trunc( 1.5) = 1
floor(-1.5) = -2, trunc(-1.5) = -1
Then, in case one of the two arguments provided is less than zero, these definitions open margin for difference between their respective results.
Mathematical definition
To Wikipedia defines "Operation Module" thus:
Data two positive numbers, a
and n
, a modulo n
is the rest of the Euclidean division of a
for n
, where a
is the dividend and n
the divisor.
When exactly one of a
or n
is negative, the definition naïve failure and programming languages differ in how these values are defined.
Mathematically, the result of the module operation is a equivalence class and any member of that class may be chosen as representative. Commonly, however, the lesser positive rest is chosen.
As other conventions of how to choose this representative are possible, the difference arises when implementing an algorithm to determine the rest of the Euclidean division. And this algorithm will depend on the programming language, since there is no consensus - all are valid according to mathematical identity. Both mod
as rem
are valid, although they produce different results.
See all the variations here and the most common implementations by programming language.
Differences in applications
One can understand mod
as an operation that maps any number to a well-defined set of values. See the application x mod 3
for x
from -5 to 5:
-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 (valores de x)
1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2 (resultados para `x mod 3`)
See that the result is cyclic and is in the whole set [0, 3[
.
However, when using rem
, we have a different result:
-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5 (valores de x)
-2, -1, 0, -2, -1, 0, 1, 2, 0, 1, 2 (resultados para `x rem 3`)
See that, for rem
, the set of possible results depends on the signal of the operands, which removes the "cyclic rigor" that exists in mod
.
Most languages offer as much rem
how much mod
(not necessarily with these names), since, as the results vary, the use can also be impacted. In some situations, only mod
fits. In others, only rem
resolve. When past operands are guaranteed to have the same signal, there is no difference.
The logic of Python is explained here. I don’t know about Rust, but it seems to me after same idea of Javascript. Remembering that remainder and module are 2 different concepts, and for positive numbers they often get confused because the results are the same (many languages, even, also confuse the names, say they implement one when it is actually another). Other related link: https://answall.com/q/446169/112052
– hkotsubo
The difference is that, in Python, the
%
is not remainder, but yes module. In Rust, the%
yes, indeed, remainder. The definitions ofmod
andrem
are different, so that arises this discrepancy.– Luiz Felipe
Thanks for the clarifications, guys. For those who come here for the same reason, know that it is possible to get the module in Rust using
(-4).rem_euclid(26)
– Lucas