DEV Community

Cover image for Why NaN !== NaN Makes Perfect Sense (I Promise)
Sylwia Laskowska
Sylwia Laskowska

Posted on

Why NaN !== NaN Makes Perfect Sense (I Promise)

No, JavaScript Isn’t Broken (Again)

I’ll admit it — sometimes I like publishing a post on the weekend, when both the internet and real life slow down a bit. But weekends are not the time for heavy technical or philosophical deep dives. It’s better to go for something lighter.

I spent a while looking for the right format. Topics like “the funniest commits ever” are fun, sure — but how many times can you do that?

And then it clicked.
This format — let’s call it JS WTF — fits my weekend vibe perfectly 😄

The first part,
Yes, true + true === 2. No, JavaScript isn't broken,
got quite a bit of attention.

And let me tell you something else — the comments under that article were absolute gold ✨
I love all of your comments. But the nerdy discussions? Pure treasure:

  • “I agree with you 99.9%, but this part needs clarification”
  • “This isn’t really computer science — that term is slightly off”
  • “You’re right, but I think JS should’ve solved this problem differently”

My heart grew three sizes that day. Please, more of this 💙

After that article and all the discussions, the next natural candidate for the series is a classic JS WTF:

NaN !== NaN

As weird as it looks, this behavior is actually fully logical — and I hope that after reading this article, it won’t feel strange to anyone anymore.

As before: if you enjoy this series, smash that like button 😄😄😄
Alright — let’s get to the point.


So… what is NaN anyway?

NaN stands for Not a Number.
In JavaScript, it’s a special numeric value of type number:

typeof NaN // "number"
Enter fullscreen mode Exit fullscreen mode

You usually get NaN as the result of an invalid numeric operation:

0 / 0         // NaN
Math.sqrt(-1) // NaN
Number("💩")  // NaN
Enter fullscreen mode Exit fullscreen mode

So far, so good.

Now comes the fun part 👀


Why is NaN !== NaN?

Let’s start with the surprise:

NaN === NaN // false
NaN !== NaN // true
Enter fullscreen mode Exit fullscreen mode

At first glance, this feels completely insane.
I mean… if something is not a number, shouldn’t it be equal to… itself?

But here’s the key idea:

NaN does not represent a specific value.
It represents an unknown or invalid result.

Think of NaN as:

“I tried to compute something, but the result is undefined.”

If two computations both fail, JavaScript has no way to prove that they failed in the same way.

This behavior is not a JavaScript quirk.
It comes straight from the IEEE-754 floating-point standard, which many languages follow.

The IEEE-754 actually allows multiple NaN representations internally. JavaScript does not expose those details, so from the language’s point of view, all NaN results are simply unknown.

So instead of pretending they’re equal, the language says:
👉 “I don’t know — so I won’t say they are.”


A useful mental model 🧠

Imagine this:

let a = Math.sqrt(-1)
let b = 0 / 0
Enter fullscreen mode Exit fullscreen mode

Both a and b are NaN.

But are they the same NaN?
According to the spec: you cannot know.

Because of that, all equality comparisons involving NaN return false — even when comparing it to itself:

NaN === NaN // false
Enter fullscreen mode Exit fullscreen mode

This rule also applies to relational comparisons:

NaN < 1   // false
NaN > 1   // false
NaN <= 1  // false
NaN >= 1  // false
Enter fullscreen mode Exit fullscreen mode

Once NaN appears, comparisons stop making sense — and JavaScript refuses to lie about it.


“Okay, but how do I check for NaN then?”

Excellent question 😄
And yes — JavaScript gives you tools.

❌ Don’t do this

value === NaN // always false
Enter fullscreen mode Exit fullscreen mode

✅ Do this instead

Number.isNaN(value)
Enter fullscreen mode Exit fullscreen mode

This is the correct and safe way.

There’s also the global isNaN(), but it performs implicit type coercion and can lead to surprises:

isNaN("123")   // false
isNaN("💩")    // true

Number.isNaN("💩") // false
Enter fullscreen mode Exit fullscreen mode

In most real-world cases, Number.isNaN() is exactly what you want.


A small plot twist 👀

JavaScript does give you one way to say that NaN equals NaN:

Object.is(NaN, NaN) // true
Enter fullscreen mode Exit fullscreen mode

Object.is() is a more precise comparison algorithm that treats special numeric edge cases differently.

For example:

Object.is(+0, -0) // false
+0 === -0         // true
Enter fullscreen mode Exit fullscreen mode

This makes Object.is() extremely useful in low-level or numeric-heavy code.


Is JavaScript the only weird one?

Short answer: nope 😌
This behavior exists in many popular languages that follow IEEE-754.

☕ Java

Double.NaN == Double.NaN // false
Enter fullscreen mode Exit fullscreen mode

Java even documents this explicitly.


🐍 Python

float('nan') == float('nan')  # False
Enter fullscreen mode Exit fullscreen mode

Same idea, same rule.


🦀 Rust

let x = f64::NAN;
x == x // false
Enter fullscreen mode Exit fullscreen mode

Rust stays very close to the floating-point standard here.


🧠 C / C++

NaN == NaN // false
Enter fullscreen mode Exit fullscreen mode

Classic behavior — nothing special.


🟢 So who behaves differently?

In environments that avoid IEEE-754 floating-point numbers altogether — for example, integer-only domains or exact decimal/rational numeric types — you may never encounter NaN.

But for mainstream, numeric-heavy languages, this behavior is the norm.

JavaScript is not broken.
It’s just being honest 😄


Why this actually makes sense

The alternative would be worse.

If NaN === NaN were true, invalid results could quietly behave like meaningful values and slip through equality checks.

By making all NaN comparisons return false, languages prevent invalid computations from masquerading as valid ones.

And bugs that fail loudly are much easier to debug than bugs that pretend everything is fine.


Final thoughts

NaN !== NaN looks like a joke at first glance.
But once you understand that NaN means “unknown result”, everything clicks.

It’s not a JavaScript WTF.
It’s a floating-point reality check 😉

Top comments (7)

Collapse
 
pascal_cescato_692b7a8a20 profile image
Pascal CESCATO

Awesome! Your WTF JS format is really refreshing. And yes, I wondered (a long time ago) why NaN === NaN returns false. You explain it, and I think to myself: "Yes, that's it!" as if there were no other possible explanation, as if it were obvious (for me, it wasn't).

Collapse
 
sylwia-lask profile image
Sylwia Laskowska

Thanks! 😄 And yes - it’s actually not a big secret at all, once you see it explained. It just feels mysterious until that “click” moment happens.

If you enjoy this kind of JS WTFs, I can highly recommend You Don’t Know JS Yet — it’s available for free on GitHub and makes for perfect long winter-evening reading!

Collapse
 
bhavin-allinonetools profile image
Bhavin Sheth

This was a really fun read — the “unknown result” framing makes NaN finally click instead of just feeling like a weird JS prank.

I especially liked how you tied it back to IEEE-754 and showed it’s not a JavaScript-only thing. That context alone clears up so many “JS is broken” takes.

The JS WTF weekend format works great too — light, nerdy, and still genuinely educational. Looking forward to the next one.

Collapse
 
sylwia-lask profile image
Sylwia Laskowska

Thank you so much! 😄 I’m really glad the “unknown result” framing helped it click — that’s exactly the moment I was hoping for.

And yes, the IEEE-754 context is such a quiet plot twist here — once you see it’s not just JavaScript, a lot of those “JS is broken” takes suddenly fall apart 😅

Happy to hear the JS WTF weekend format works for you — more nerdy WTFs coming soon! 🚀

Collapse
 
benjamin_nguyen_8ca6ff360 profile image
Benjamin Nguyen

Nice! I love that you make your articles so personal with the emoji and ease to read :). C++ is the hard language to learn but it gets easier afterward. I love C++ and Python.

Collapse
 
sylwia-lask profile image
Sylwia Laskowska

Thank you! 😊 I’m really happy you like the personal tone and emojis - that means a lot to me.

And yes, C++ is definitely a tough one 😅 but once it “clicks,” a lot of things in other languages start to make much more sense. Great combo with Python too!

Collapse
 
benjamin_nguyen_8ca6ff360 profile image
Benjamin Nguyen

Lol! it is so true :). Thank you!