Fundamentalsbeginner
NaN and Number Edge Cases
Understanding NaN's self-inequality, floating-point precision issues, and safe ways to check for numeric validity.
NaN means "not a number" and is itself of type number. Its defining property is that it is not equal to itself.
NaN is like a person with no ID who can never prove they're the same person twice, even to themselves. Floating-point imprecision is like trying to represent 1/3 in decimal — you eventually have to round, and that tiny rounding error can snowball.
Key Concepts
1
NaN === NaN // false
[NaN].includes(NaN) // true -- includes uses SameValueZero
[NaN].indexOf(NaN) // -1 -- indexOf uses ===2
So testing for it needs a dedicated function.
Number.isNaN(x) // true only for actual NaN
isNaN(x) // coerces first -- isNaN('abc') is true
Object.is(x, NaN) // also correct3
The global isNaN converts its argument before testing, so it reports true for any value that is not a
number at all. Number.isNaN is the one to use.
isNaNtrueNumber.isNaN
4
Where NaN comes from.
0 / 0
parseInt('abc')
Math.sqrt(-1)
undefined + 1NaN
5
And it propagates: any arithmetic involving NaN produces NaN, so one bad value silently poisons a whole
calculation chain.
NaN
6
Floating point is the other edge.
0.1 + 0.2 === 0.3 // false
0.1 + 0.2 // 0.300000000000000047
Not a JavaScript flaw — it is IEEE 754 binary floating point, and the same in almost every language. A
tenth cannot be represented exactly in binary, just as a third cannot in decimal.
8
Comparing with a tolerance.
Math.abs(a - b) < Number.EPSILON * Math.max(1, Math.abs(a), Math.abs(b))9
For money, do not use floats at all. Work in the smallest unit as integers:
10
1999 cents, not 19.9911
That is what financial systems do, and it removes the problem rather than tolerating it.
12
Integer safety.
Number.MAX_SAFE_INTEGER // 9007199254740991
Number.MAX_SAFE_INTEGER + 1 === Number.MAX_SAFE_INTEGER + 2 // true13
Beyond 2^53 integers are no longer exact. A 64-bit id from a backend, sent as a JSON number, can arrive
subtly wrong — which is why APIs send large ids as strings, and BigInt exists.
BigInt
14
The other values to recognise.
Infinity, -Infinity 1/0, and overflow
-0 0 === -0 is true; Object.is(0, -0) is false
Number.isInteger(x)
Number.isFinite(x) does not coerce, unlike global isFinite15
What the interviewer is probing.1. "Why is NaN !== NaN?" Probing: the specification. Stalls: "A bug." Moves up: it is
defined as not equal to anything including itself; use Number.isNaN or Object.is to test for it.
16
2. "Why is 0.1 + 0.2 not 0.3?" Probing: floating point. Stalls: "Rounding error." *Moves
up:* IEEE-754 doubles cannot represent those decimals exactly; compare within an epsilon or use
integers of the smallest unit.
17
3. "What is the largest safe integer?" Probing: precision limits. Stalls: "Infinity." *Moves
up:* 2^53 - 1, Number.MAX_SAFE_INTEGER — beyond it integers lose precision, which is why BigInt
exists.
18
4. "How does Number.isNaN differ from the global isNaN?" Probing: coercion. Stalls: "They
are the same." Moves up: the global coerces first, so isNaN("abc") is true; Number.isNaN only
returns true for actual NaN.