You may have read that JavaScript’s %
operator is a remainder operator, not a modulo operator. This blog post explains what that means.
Let’s assume there are two operators that are very similar:
rem
mod
If both operands have the same signs, then the operators produce the same results:
> 5 rem 3
2
> 5 mod 3
2
> -5 rem -3
-2
> -5 mod -3
-2
If, however, they have different signs, then the result of rem
has the same sign as the first operand, while the result of mod
has the same sign as the second operand:
> -5 rem 3
-2
> -5 mod 3
1
> 5 rem -3
2
> 5 mod -3
-1
Why is that? Read on.
rem
We’ll first take a closer look at the rem
operator:
dividend rem divisor
In order to compute the remainder, we use the following two equations:
dividend = divisor * quotient + remainder
|remainder| < |divisor|
dividend
, divisor
, quotient
, and remainder
are all integers (for the sake of this blog post).
In order to compute the remainder we must find the quotient
:
remainder = dividend - divisor * quotient
And we do so by dividing the dividend
by the divisor
. Math.trunc()
ensures that the resulting quotient
is an integer.
Example 1: 5 rem 3 === 2
const dividend = 5;
const divisor = 3;
const quotient = Math.trunc(dividend / divisor);
assert.equal(quotient, 1);
Example 2: -5 rem 3 === -2
const dividend = -5;
const divisor = 3;
const quotient = Math.trunc(dividend / divisor);
assert.equal(quotient, -1);
mod
The modulo operator is based on the same equations, but it uses Math.floor()
to compute quotients:
The reason for that is that Math.trunc()
and Math.floor()
produce the same results for positive numbers, but different results for negative numbers.
Example 3: 5 mod 3 === 2
(dividend is 5, divisor is 3)
const dividend = 5;
const divisor = 3;
const quotient = Math.floor(dividend / divisor);
assert.equal(quotient, 1);
Example 4: -5 mod 3 === 1
(dividend is −5, divisor is 3)
const dividend = -5;
const divisor = 3;
const quotient = Math.floor(dividend / divisor);
assert.equal(quotient, -2);
Modulo can also be viewed as an operation that maps an arbitrary number into a given range – for example:
x mod 3
maps x
into the range
[0,3) = {0,1,2}
That is, zero is included, 3 is excluded.
If x
is already inside the range, performing the mapping is simple:
> 0 mod 3
0
> 2 mod 3
2
If x
is greater than or equal to the upper boundary of the range, then the upper boundary is subtracted from x
until it fits into the range:
> 4 mod 3
1
> 7 mod 3
1
That means we are getting the following mapping for non-negative integers:
0 1 2 3 4 5 6 7
0 1 2 0 1 2 0 1
This is how the mapping is extended so that it also covers negative integers:
-7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7
2 0 1 2 0 1 2 0 1 2 0 1 2 0 1
> -1 mod 3
2
> -3 mod 3
0
> -4 mod 3
2
x rem 3
maps x
as follows:
-7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7
-1 0 -2 -1 0 -2 -1 0 1 2 0 1 2 0 1
x mod -3
maps x
to the range (-3, 0]
-7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7
-1 0 -2 -1 0 -2 -1 0 -2 -1 0 -2 -1 0 -2
x rem -3
has the following mapping (the same as x rem 3
):
-7 -6 -5 -4 -3 -2 -1 0 1 2 3 4 5 6 7
-1 0 -2 -1 0 -2 -1 0 1 2 0 1 2 0 1
The ECMAScript specification uses modulo several times – for example:
To convert the operands of the >>>
operator to unsigned 32-bit integers (via x mod 2**32
):
> 2**32 >>> 0
0
> (2**32)+1 >>> 0
1
> (-1 >>> 0) === (2**32)-1
true
To convert arbitrary numbers so that they fit into Typed Arrays. For example, x mod 2**8
is used to convert numbers to unsigned 8-bit integers (after first converting them to integers):
const tarr = new Uint8Array(1);
tarr[0] = 256;
assert.equal(tarr[0], 0);
tarr[0] = 257;
assert.equal(tarr[0], 1);
Which of rem
and mod
is supported and how depends on the programming language:
%
operator is a remainder operator.%
operator is a modulo operator.If you want to read more about remainder and modulo:
%
is computed via “truncating division”.