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:
remmodIf 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”.