Discover why NaN !== NaN in JavaScript. Learn about NaN behavior, proper ways to check for NaN, and the difference between isNaN() and Number.isNaN().

Discover why NaN !== NaN in JavaScript. Learn about NaN behavior, proper ways to check for NaN, and the difference between isNaN() and Number.isNaN().

In JavaScript:

NaN === NaN  // false
NaN !== NaN  // true

NaN is the only value in JavaScript that doesn't equal itself. This follows the IEEE 754 floating-point standard, but it's counterintuitive.

What Is NaN?

NaN represents an invalid numeric result:

Math.sqrt(-1)      // NaN (no real square root of negative)
parseInt('abc')    // NaN (can't parse non-numeric string)
0 / 0              // NaN (mathematically undefined)
Infinity - Infinity  // NaN

Any operation that can't produce a valid number returns NaN.

Why NaN !== NaN

The IEEE 754 standard defines NaN as not equal to anything, including itself. The reasoning: NaN represents an unknown or invalid value. Two unknown values can't be assumed equal.

const x = 0 / 0;  // NaN
const y = 0 / 0;  // NaN
x === y;  // false - different operations, different unknowns

From a mathematical perspective, comparing undefined values doesn't make sense.

Checking for NaN

Since value === NaN doesn't work, use Number.isNaN():

Number.isNaN(NaN)        // true
Number.isNaN(0 / 0)      // true
Number.isNaN('abc')      // false (string, not NaN)
Number.isNaN(undefined)  // false

Or use the self-inequality property:

function isNaNValue(value) {
    return value !== value;
}

isNaNValue(NaN)  // true

This works because NaN is the only value that doesn't equal itself.

isNaN() vs Number.isNaN()

JavaScript has two NaN-checking functions:

isNaN() - coerces to number first:

isNaN('abc')      // true (coerces to NaN)
isNaN('123')      // false (coerces to 123)
isNaN(undefined)  // true (coerces to NaN)
isNaN({})         // true (coerces to NaN)

Number.isNaN() - no coercion, checks actual NaN:

Number.isNaN('abc')      // false (string, not NaN)
Number.isNaN('123')      // false
Number.isNaN(undefined)  // false
Number.isNaN({})         // false
Number.isNaN(NaN)        // true

Use Number.isNaN() for reliable NaN detection. isNaN() is misleading.

How NaN Propagates

Operations with NaN produce NaN:

NaN + 5        // NaN
NaN * 2        // NaN
NaN / 10       // NaN
Math.max(1, NaN)  // NaN

This is "NaN poisoning"—one NaN corrupts the entire calculation:

function average(numbers) {
    const sum = numbers.reduce((a, b) => a + b, 0);
    return sum / numbers.length;
}

average([1, 2, NaN, 4]);  // NaN

A single NaN makes the average NaN.

Checking for Numeric Values

To verify a value is a valid number:

function isValidNumber(value) {
    return typeof value === 'number' && !Number.isNaN(value);
}

isValidNumber(123)       // true
isValidNumber(NaN)       // false
isValidNumber('123')     // false (string)
isValidNumber(Infinity)  // true

For finite numbers only:

Number.isFinite(123)      // true
Number.isFinite(Infinity) // false
Number.isFinite(NaN)      // false
Number.isFinite('123')    // false (no coercion)

Common Sources of NaN

Parsing invalid input:

parseInt('abc')     // NaN
parseFloat('xyz')   // NaN
Number('not a number')  // NaN

Math operations with non-numbers:

'string' * 2       // NaN
undefined + 5      // NaN
{} - 10            // NaN

Invalid math operations:

Math.sqrt(-1)      // NaN
0 / 0              // NaN
Infinity / Infinity  // NaN

Avoiding NaN in Calculations

Validate input:

function safeDivide(a, b) {
    if (b === 0) return null;  // Or throw error
    return a / b;
}

Provide defaults:

const value = parseFloat(input) || 0;

Filter out NaN values:

const numbers = [1, 2, NaN, 4, NaN, 5];
const valid = numbers.filter(n => !Number.isNaN(n));
// [1, 2, 4, 5]

NaN in Comparisons

NaN is neither greater than nor less than any value:

NaN > 5   // false
NaN < 5   // false
NaN >= 5  // false
NaN <= 5  // false

Sorting arrays with NaN can produce unexpected results:

[1, NaN, 3, 2].sort((a, b) => a - b);
// NaN comparisons return NaN, breaking sort

Filter NaN before sorting or handle in comparator:

function compare(a, b) {
    if (Number.isNaN(a)) return 1;   // Push NaN to end
    if (Number.isNaN(b)) return -1;
    return a - b;
}

[1, NaN, 3, 2].sort(compare);  // [1, 2, 3, NaN]

typeof NaN

Despite being "Not-a-Number," typeof NaN is 'number':

typeof NaN  // 'number'

This is because NaN is a numeric type representing an invalid number—it's still part of the number type system.

Object.is and NaN

Object.is() treats NaN specially:

Object.is(NaN, NaN)  // true
NaN === NaN          // false

Object.is() uses "same-value equality," which considers NaN equal to itself. This is useful for certain algorithms.

Returning NaN from Functions

Returning NaN signals invalid input or computation failure:

function parseAge(input) {
    const age = parseInt(input);
    if (Number.isNaN(age) || age < 0 || age > 150) {
        return NaN;
    }
    return age;
}

But consider if null or throwing an error is clearer.

JSON and NaN

JSON doesn't support NaN. It becomes null when serialized:

JSON.stringify({ value: NaN })  // '{"value":null}'

Deserializing gives null, not NaN. Handle this if you need to preserve NaN through serialization.

Performance

Checking value !== value is slightly faster than Number.isNaN(value), but the difference is negligible. Use Number.isNaN() for clarity.

Historical Context

The global isNaN() predates ES6. Its coercion behavior surprised developers. Number.isNaN() was added in ES6 to provide predictable behavior without coercion.

Further Reading

MDN's NaN documentation covers all behaviors and edge cases.

The IEEE 754 standard explains why NaN doesn't equal itself in floating-point arithmetic.

Axel Rauschmayer's post on NaN and Infinity explores JavaScript's special numeric values.

NaN's self-inequality is one of JavaScript's most notorious quirks.

Wear the code

Product mockup

NaN !== NaN; Developer T-Shirt (JavaScript Edition — Dark Mode)

£25.00

View product
Product mockup

NaN !== NaN; Developer T-Shirt (JavaScript Edition — Light Mode)

£25.00

View product

0 comments

Leave a comment

Please note, comments need to be approved before they are published.