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"
🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung 🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung

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

0 / 0         // NaN
Math.sqrt(-1) // NaN
Number("💩")  // NaN
🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung 🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung

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
🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung 🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung

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
🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung 🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung

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
🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung 🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung

This rule also applies to relational comparisons:

NaN < 1   // false
NaN > 1   // false
NaN <= 1  // false
NaN >= 1  // false
🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung 🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung

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
🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung 🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung

✅ Do this instead

Number.isNaN(value)
🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung 🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung

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
🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung 🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung

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
🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung 🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung

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
🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung 🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung

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
🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung 🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung

Java even documents this explicitly.


🐍 Python

float('nan') == float('nan')  # False
🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung 🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung

Same idea, same rule.


🦀 Rust

let x = f64::NAN;
x == x // false
🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung 🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung

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


🧠 C / C++

NaN == NaN // false
🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung 🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung

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 (48)

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
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).

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
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!

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
pascal_cescato_692b7a8a20 profile image
Pascal CESCATO

I just checked out You Don’t Know JS Yet — it does look interesting, but it doesn’t have that storytelling touch that makes your content stand out. And honestly, JavaScript isn’t enough of a passion for me to dive into something that heavy. I’d much rather follow your series; it’s much more refreshing!

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung Thread
 
sylwia-lask profile image
Sylwia Laskowska

Haha, thanks Pascal 😄 that’s a huge compliment!

You’re totally right - You Don’t Know JS Yet is more of a deep-dive reference than a storytelling ride, and it’s definitely not for everyone.
Maybe I should team up with Kyle and do a light editorial pass: same knowledge, more emojis, fewer mental breakdowns 😂

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung Thread
 
pascal_cescato_692b7a8a20 profile image
Pascal CESCATO

That's why I'm always drawn to your posts… and those of @aaron_rose_0787cc8b4775a0. Yours are truly refreshing, and his read like a captivating novel. With the two of you, I'm not really reading "technical articles"—I'm reading engaging articles.

Most of the time, I only delve into an article when it truly captivates me… the rest I skim. Yours always do.

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung Thread
 
aaron_rose_0787cc8b4775a0 profile image
Aaron Rose

What a lovely thing to say, Pascal. I’m just glad to be here contributing to Sylwia’s excellent post. She sets a high bar for all of us! I always look forward to seeing your name in posts and comments. Cheers! 🙏✨❤

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung Thread
 
sylwia-lask profile image
Sylwia Laskowska

Awwww @pascal_cescato_692b7a8a20 @aaron_rose_0787cc8b4775a0 you are too nice!!! 🥹🥰💖

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung Thread
 
2colours profile image
2colours

It takes nothing from this post but I think You Don't Know JS is actually fairly entertaining and a considerably "light read" for something that technical

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung Thread
 
pascal_cescato_692b7a8a20 profile image
Pascal CESCATO

You're right, and that's Sylwia's DNA… her writer's DNA, at least 🙃

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
drake_kuwait_ddea6374d481 profile image
Drake Kuwait

Totally agree with you 👍

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
codingpanel profile image
Coding Panel

Loved this explanation, Sylwia! 😄 NaN !== NaN always felt like one of those “JavaScript is broken” moments, but your breakdown makes it click—seeing NaN as “unknown” really clears things up. I also didn’t realize Object.is() could treat NaN as equal—super useful tip for numeric-heavy code! 👏

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
ingosteinke profile image
Ingo Steinke, web developer

Fun and educational, and another reason to use either TypeScript or strict eslint recommendations.

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
sylwia-lask profile image
Sylwia Laskowska

Exactly! 😄
I honestly can’t even remember the last time I wrote something in pure JS… okay, I can - but I choose to repress that memory 😅

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
ingosteinke profile image
Ingo Steinke, web developer

I remember a project using very strict and opinionated eslint rules, probably based on airbnb recommendations, but still EcmaScript, not TypeScript. That included a long list of idioms to avoid, like using the plus sign to concatenate strings (for the risk of mixing it with numbers) and even using i++ was discouraged for allegedly facilitating off-by-one errors.

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung Thread
 
sylwia-lask profile image
Sylwia Laskowska

Oh yes 😄 I remember those Airbnb recommendations very well… and how many juniors cried over them 😅

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
2colours profile image
2colours

What does Typescript bring to floating-point arithmetics?

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
francistrdev profile image
FrancisTRᴅᴇᴠ (っ◔◡◔)っ

This is funny, but it makes sense in a technical standpoint.

The way I think "NaN" in my view is "There are many different categories that falls under NaN, in where NaN is not a universal things". Like your example where an emoji is NaN and a string "123" is a NaN.

Sure, both are NaN, but it is about what causes the NaN for NaN !== NaN to be true since they are different. Great job!

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
sylwia-lask profile image
Sylwia Laskowska

Exactly - that’s a really good way to think about it 😄
Different “reasons” for NaN, same result, but not something you can meaningfully compare. Thanks a lot! 🙌

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
doogal profile image
Doogal Simpson

Great point bringing up IEEE-754. It’s easy to dunk on JavaScript for these quirks, but when you see the same behavior in Rust or C++, it shifts the conversation from 'weird language behavior' to 'foundational computer science.' It’s a reminder that as much as we abstract things away with high-level code, we’re still ultimately operating within the constraints of how silicon handles math.

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
sylwia-lask profile image
Sylwia Laskowska

Exactly! 😄 That’s precisely the point.
Once you see the same behavior in Rust or C++, it stops being “JavaScript being weird” and starts being “this is how floating-point math works, period.”

And honestly… I don’t know why JavaScript ended up as the main scapegoat here either 😂 It just happens to expose these rules more openly than some other languages.

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
htho profile image
Hauke T.

I like your Rust example, where the same variable is unequal to the same variable. LMAO

let x = f64::NAN;
x == x // false
🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung 🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
sylwia-lask profile image
Sylwia Laskowska

Exactly! 😄 It’s not a JS-only thing at all. NaN is just… consistently weird in many languages 😂

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
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.

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
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! 🚀

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
shemith_mohanan_6361bb8a2 profile image
shemith mohanan

This was a really enjoyable read — clear, light, and surprisingly satisfying 😄
The “NaN means unknown, not a value” framing makes everything click. I especially liked the point about languages choosing honesty over convenience. Great example of a JS “WTF” that actually makes sense once you zoom out.

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
sylwia-lask profile image
Sylwia Laskowska

Thank you! 😄 I’m really happy that framing resonated with you — “unknown, not a value” is one of those ideas that suddenly makes everything fall into place.

And yes, that trade-off between honesty and convenience is such a great example of how many so-called JS “WTFs” actually make a lot of sense once you zoom out a bit. Glad you enjoyed it! 🚀

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
itsugo profile image
Aryan Choudhary

This was a really good read Sylwia. The “NaN as unknown, not a value” framing is one of those explanations that makes everything suddenly feel obvious in hindsight. It turns something that feels like a JS prank into a really clean mental model.

I also really liked learning about IEEE-754 and other languages, it quietly reframes this from “JavaScript weirdness” into “floating-point reality,” which is way more useful for how people actually think about bugs.

For future JS WTFs, are there any other “looks broken but is actually logical” behaviors you’re especially excited to cover?

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
sylwia-lask profile image
Sylwia Laskowska

Thank you so much! 😄 I’m really glad that mental model landed - that “oh, of course” moment is the best kind of JS WTF outcome.

As for what’s next: I’m definitely thinking about some “weird” array and object behaviors 👀
And honestly… JavaScript itself is an endless source of inspiration for this series 😂

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
mikulas_bartacek_cd3cacc2 profile image
Mikulas Bartacek

Nice! I knew that JS do this, but i didn't know why. I thought that it has to do something with value representation (object reference comparing etc...). Now i know that it's directly from IEEE 754. thanks

🏆 Live Soccer TV: sylwia lask/why nan nan makes perfect sense i promise lke - Jadwal Siaran Langsung
 
sylwia-lask profile image
Sylwia Laskowska

Exactly — that’s a very common intuition 😊
Glad the IEEE-754 connection cleared it up. Thanks for reading! 🙌

Some comments may only be visible to logged-in visitors. Sign in to view all comments.