Math.abs(x) returns the magnitude of x, discarding the sign. The interesting part is what it does to anything that is not a number, because it will try rather than complain.
This is a one-line answer with a long tail. Math.abs(-5) is 5 and nobody needs a blog post about that.
What is worth knowing is that Math.abs coerces its argument, which means it silently accepts strings, arrays, null and undefined and returns something for each — occasionally a number you did not expect, and often NaN that flows onward until it corrupts a total three functions later.
Table of contents
- The basics, including the odd cases
- Coercion, and where NaN comes from
- BigInt throws
- Practical uses beyond making a number positive
- Faster alternatives, and why to ignore them
- Where it goes wrong in production
- How this fits the rest of the stack
- FAQ
The basics, including the odd cases
Math.abs(-5) // 5
Math.abs(5) // 5
Math.abs(-3.7) // 3.7
Math.abs(0) // 0
Math.abs(-0) // 0
Math.abs(-Infinity) // Infinity
Math.abs(-0) returning 0 is correct and worth a note, because -0 exists in JavaScript and is one of the few places where Object.is and === disagree:
-0 === 0 // true
Object.is(-0, 0) // false
Object.is(Math.abs(-0), 0) // true -- abs really does normalise it
-0 shows up from operations like -1 * 0 or rounding a small negative number, and it can produce genuinely puzzling results — 1 / -0 is -Infinity. Passing a value through Math.abs is a reasonable way to normalise it away when the sign does not matter.
Coercion, and where NaN comes from
Math.abs converts its argument to a number first, using the same rules as the unary + operator:
Math.abs("-5") // 5 -- numeric string converts
Math.abs("abc") // NaN -- non-numeric string does not
Math.abs(null) // 0 -- null converts to 0
Math.abs(undefined) // NaN
Math.abs([]) // 0 -- empty array converts to 0
Math.abs([5]) // 5 -- single-element array converts to that element
Math.abs([1, 2]) // NaN
Math.abs({}) // NaN
Math.abs() // NaN -- no argument
Math.abs(null) being 0 and Math.abs([]) being 0 are the dangerous ones. A missing value from an API response becomes a legitimate-looking zero rather than an error, and zero is a value that passes most validation.
The problem with the NaN results is that NaN propagates silently. Every arithmetic operation involving it produces NaN, so the failure surfaces far from its cause — typically as NaN rendered in the UI after the numbers have been through a reduce.
If the input comes from outside your code, check it:
function magnitude(value) {
if (typeof value !== 'number' || !Number.isFinite(value)) {
throw new TypeError(`expected a finite number, got ${typeof value}: ${value}`);
}
return Math.abs(value);
}
Number.isFinite rather than the global isFinite — the global one coerces, which reintroduces exactly the problem you are guarding against.
BigInt throws
Unlike everything else, BigInt does not coerce — it throws:
Math.abs(5n) // TypeError: Cannot convert a BigInt value to a number
This is deliberate. Converting a BigInt to a Number could lose precision silently, so the language refuses. Since Math has no BigInt equivalents, write it yourself:
const absBigInt = (n) => (n < 0n ? -n : n);
absBigInt(-9007199254740993n) // 9007199254740993n
Relevant if you handle large IDs, currency in minor units, or anything past Number.MAX_SAFE_INTEGER. A generic numeric helper that does not account for BigInt will throw the moment one arrives.
Practical uses beyond making a number positive
Where Math.abs actually earns its place in real code:
Distance between two values, without caring which is larger:
const drift = Math.abs(expected - actual);
if (drift > tolerance) { /* alert */ }
Floating-point comparison. Never compare floats with ===:
// 0.1 + 0.2 === 0.3 is false
const nearlyEqual = (a, b, eps = Number.EPSILON * 8) =>
Math.abs(a - b) < eps;
For values far from zero, a relative comparison is more honest than an absolute epsilon — Math.abs(a - b) <= eps * Math.max(Math.abs(a), Math.abs(b)) — because Number.EPSILON is the gap between representable numbers near 1, not everywhere.
Clamping a signed range, and sorting by magnitude:
const clamped = Math.sign(v) * Math.min(Math.abs(v), limit);
const byMagnitude = values.sort((a, b) => Math.abs(b) - Math.abs(a));
Faster alternatives, and why to ignore them
You will find bitwise tricks presented as optimisations:
const fastAbs = (n) => (n ^ (n >> 31)) - (n >> 31); // 32-bit integers only
Do not use this. It works only for 32-bit signed integers, silently produces wrong answers for floats and for anything beyond that range, and modern engines optimise Math.abs to a single machine instruction anyway. You are trading correctness for a speedup that does not exist.
The one alternative with a real use is Math.sign, when you want the direction rather than the magnitude — and the two together (Math.sign(v) * Math.abs(v) reconstructing v) is a readable way to express operations that treat sign and magnitude separately.
Where it goes wrong in production
The realistic failure is not the function — it is NaN produced from unvalidated input at a boundary, propagating through a calculation, and surfacing as a nonsense figure in a report.
Validate at the edges: when parsing a JSON body, reading a query parameter, or pulling a numeric column that might be null. Inside those boundaries, values can be trusted and Math.abs is exactly as simple as it looks.
A JavaScript API doing this validation deploys on RunxBuild from a repository as a Node service, with a build log, a live route, runtime logs and rollback to the previous deploy — so when a NaN does reach production, the request that produced it and the deploy that shipped it are in the same place.
How this fits the rest of the stack
Math.abs is one call with a coercion step attached, and that step is where the bugs come from: null becomes 0, undefined becomes NaN, and BigInt throws. Validate numeric input at the boundary with Number.isFinite, use Math.abs for float comparison and drift checks, and ignore the bitwise trick. When the service doing that validation needs a database behind it, the RunxBuild hosting calculator shows both as separate line items.
Useful related references:
- Create a Symbolic Link in Linux: ln -s, and the Absolute-Path Rule That Saves You
- Python or: Short-Circuits, Truthiness, and the Default-Value Trap
- Python Inline If: Select a Value Without Compressing the Logic
- Services on RunxBuild
FAQ
How do I get the absolute value of a number in JavaScript?
Call Math.abs(x). It returns the magnitude with the sign discarded, so Math.abs(-5) is 5. There is no absolute-value method on the Number prototype — it lives on the Math object.
Why does Math.abs return NaN?
Because the argument could not be converted to a number. Math.abs("abc"), Math.abs(undefined), Math.abs({}) and Math.abs() all return NaN, and NaN propagates silently through every subsequent calculation. Validate input with Number.isFinite at the boundary.
Does Math.abs work with BigInt?
No, it throws a TypeError. BigInt does not coerce to Number because that could lose precision. Write your own: const absBigInt = n => n < 0n ? -n : n.
What does Math.abs(-0) return?
It returns 0, normalising away the negative zero. This matters occasionally because -0 behaves differently in some contexts — Object.is(-0, 0) is false and 1 / -0 is -Infinity — so passing values through Math.abs is a way to eliminate it.
Is there a faster alternative to Math.abs?
No useful one. The bitwise trick you will find online works only for 32-bit signed integers and silently returns wrong answers otherwise, while modern engines compile Math.abs to a single instruction. Use Math.abs.