Arithmetic Operators
Arithmetic operators are how JavaScript does math, from adding two numbers to computing powers and remainders. They look familiar from school, but JavaScript adds a few wrinkles worth knowing: a single + that doubles as string concatenation, prefix versus postfix increment, and the floating-point quirks that surprise newcomers. Mastering these — and the precedence rules that decide what runs first — keeps your calculations correct and your code readable.
The basic operators
JavaScript has the five operators you’d expect, plus exponentiation (**, added in ES2016). All of them work on numbers and coerce non-numeric operands to numbers where possible.
| Operator | Name | Example | Result |
|---|---|---|---|
+ | Addition | 7 + 3 | 10 |
- | Subtraction | 7 - 3 | 4 |
* | Multiplication | 7 * 3 | 21 |
/ | Division | 7 / 3 | 2.333… |
% | Remainder | 7 % 3 | 1 |
** | Exponentiation | 2 ** 10 | 1024 |
const a = 7;
const b = 3;
console.log(a + b); // 10
console.log(a / b); // 2.3333333333333335
console.log(a % b); // 1
console.log(2 ** 10); // 1024
Output:
10
2.3333333333333335
1
1024
Note that / always produces a floating-point result — there is no separate integer division. To truncate toward zero, use Math.trunc(a / b); to round down, use Math.floor.
Remainder and exponentiation
The % operator returns the remainder after division, not a true mathematical modulo. The result takes the sign of the dividend (the left operand), which matters for negative numbers.
console.log(10 % 3); // 1
console.log(-10 % 3); // -1 (sign follows -10)
console.log(10 % -3); // 1
// Common use: detect even/odd
const isEven = (n) => n % 2 === 0;
console.log(isEven(4)); // true
For exponentiation, ** is right-associative, so 2 ** 3 ** 2 evaluates as 2 ** (3 ** 2) = 2 ** 9 = 512. It replaces the older Math.pow(2, 9) and reads more naturally.
Gotcha: A unary minus directly in front of a base is a syntax error:
-2 ** 2is rejected. Write(-2) ** 2(which is4) or-(2 ** 2)(which is-4) to be explicit.
Unary plus and minus
+ and - also work as unary operators on a single value. Unary minus negates a number; unary plus is the fastest, terse way to coerce a value to a number — it’s equivalent to Number(x).
const str = "42";
console.log(+str); // 42 (number)
console.log(typeof +str); // "number"
console.log(-str); // -42
console.log(+""); // 0
console.log(+"abc"); // NaN
console.log(+true); // 1
Output:
42
number
-42
0
NaN
1
Increment and decrement
++ adds one and -- subtracts one. Each comes in two forms: prefix (++x) changes the variable and returns the new value; postfix (x++) returns the old value, then changes the variable. The difference only shows when you use the result in the same expression.
let x = 5;
console.log(x++); // 5 → logs old value, then x becomes 6
console.log(x); // 6
let y = 5;
console.log(++y); // 6 → y becomes 6 first, then logs
console.log(y); // 6
Output:
5
6
6
6
Tip: When the return value isn’t used (e.g. a
forloop counter or a standalone statement), prefix and postfix behave identically — pick whichever your team prefers. These operators only work on variables and properties, never on literals:5++is an error.
Operator precedence
When operators mix in one expression, precedence decides the order. Exponentiation binds tighter than *, /, and %, which bind tighter than + and -. Operators of equal precedence (except **) evaluate left to right. Parentheses always override the defaults.
console.log(2 + 3 * 4); // 14, not 20
console.log(2 ** 3 * 2); // 16 ((2**3) * 2)
console.log((2 + 3) * 4); // 20
console.log(10 - 4 - 2); // 4 (left-to-right)
When in doubt, add parentheses. They cost nothing at runtime and make intent obvious to the next reader.
Number quirks
JavaScript numbers are IEEE-754 double-precision floats, so some decimals can’t be represented exactly. The classic example surprises everyone:
console.log(0.1 + 0.2); // 0.30000000000000004
console.log(0.1 + 0.2 === 0.3); // false
// Compare with a tolerance instead
const almostEqual = (a, b) => Math.abs(a - b) < Number.EPSILON;
console.log(almostEqual(0.1 + 0.2, 0.3)); // true
// Or round for display
console.log((0.1 + 0.2).toFixed(2)); // "0.30"
Output:
0.30000000000000004
false
true
0.30
A few other values to know: division involving 0 or non-numbers yields special results rather than throwing. 1 / 0 is Infinity, -1 / 0 is -Infinity, and 0 / 0 is NaN. Any arithmetic touching NaN produces NaN, and NaN is famously not equal to itself — use Number.isNaN(x) to test for it. For exact decimal money math, use integers (work in cents) or a library like decimal.js. For integers beyond Number.MAX_SAFE_INTEGER (2^53 − 1), use BigInt literals such as 10n ** 20n.
Best Practices
- Reach for
**overMath.pow, and preferMath.trunc/Math.floorfor integer division instead of bit hacks. - Never compare floating-point results with
===; useNumber.EPSILONor.toFixed()for display. - Use
+valuefor quick numeric coercion, butNumber(value)orparseInt/parseFloatwhen clarity matters. - Parenthesize mixed-operator expressions rather than relying on memory of precedence tables.
- Avoid postfix
++/--inside larger expressions where the read-then-mutate behavior can confuse readers. - Handle money in integer cents (or
BigInt) to sidestep rounding errors entirely. - Guard against
NaNandInfinityfrom untrusted input withNumber.isFinite()before using a computed value.