JavaScript's % operator returns the remainder. It is not a mathematical modulo operator that always returns a non-negative result. The result usually follows the sign of the dividend, so -1 % 5 is -1, not 4.
Basic behavior
12 % 5 // 2
10 % 2 // 0
7 % 3 // 1
-7 % 3 // -1
7 % -3 // 1
With Number values, division by zero returns NaN:
10 % 0 // NaN
Non-negative modulo
Circular indexes, weekdays, and angles often need a result in [0, n):
function modulo(value, divisor) {
return ((value % divisor) + divisor) % divisor
}
modulo(-1, 5) // 4
modulo(6, 5) // 1
Before calling this helper, make sure divisor is a non-zero finite number that matches your business rule.
Odd and even checks
function isEven(value) {
return value % 2 === 0
}
For user input, validate integer-ness first:
function isEvenInteger(value) {
return Number.isInteger(value) && value % 2 === 0
}
Circular indexes
function nextIndex(current, length) {
if (length <= 0) throw new Error("length must be positive")
return (current + 1) % length
}
function previousIndex(current, length) {
if (length <= 0) throw new Error("length must be positive")
return modulo(current - 1, length)
}
Moving forward does not produce a negative number. Moving backward can, so use the modulo helper.
Angle normalization
function normalizeDegrees(degrees) {
return modulo(degrees, 360)
}
normalizeDegrees(450) // 90
normalizeDegrees(-90) // 270
For animation, normalization only solves the range. You may still need to handle interpolation direction from 359 to 0.
BigInt
BigInt also supports remainder, but both sides must be BigInt:
10n % 3n // 1n
This throws:
10n % 3
BigInt division by 0n throws an error instead of returning NaN.
Floating-point caution
0.3 % 0.1
may not produce an exact zero because of binary floating-point representation. For money or exact decimal steps, convert to the smallest integer unit or use an appropriate decimal strategy.
Related FreeMac guides
- For loops and indexes, read JavaScript Array Methods: map, splice, sort, and for...of.
- For scope in loops, read Why var and let Behave Differently in for Loops.
- For async execution, read JavaScript Event Loop: Tasks, Microtasks, and Rendering.
Continue reading
JavaScript Event Loop: Tasks, Microtasks, and Rendering
Understand the browser event loop through the call stack, tasks, microtasks, Promise callbacks, queueMicrotask, async/await, rendering opportunities, and long tasks.
7 min readWhy var and let Behave Differently in for Loops
Use the classic JavaScript closure example to understand why var in a for loop prints 3 3 3, while let creates per-iteration bindings and prints 0 1 2.
8 min readJavaScript var vs let: Function Scope, Block Scope, and Closures
Understand the difference between var and let in JavaScript through function scope, block scope, for loops, closures, and why let avoids the classic 3 3 3 result.
Subscribe to FreeMac
Weekly picks: free Mac software reviews, trusted source updates, alternatives, and low-friction guides.