なぜ NaN === NaN は false なのか

NaN は特定の値ではなく「数値として表せなかった」という印です。そのため === では等しくなりません。includes や Set では等しく扱われる理由も、実行して確かめられます。

実行して検証済み処理系Node 22.22.3対応ES1難易度●●●○○目安8更新

NaN特定の値ではなく、「数値として表せなかった」という印だからです。 計算が失敗した2つの結果が、同じ失敗だとは限りません。 だから比べても等しくなりません。これは JavaScript ではなく IEEE 754 が決めています。

example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
false false
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
false false false

0 / 0Number('あ') はどちらも NaN になりますが、同じ失敗ではありません

仕様ではどう書かれているか

=== の中身は IsStrictlyEqual という手続きです。 数値同士のときは Number::equal を呼び、そこにこう書いてあります。

引数のどちらかが NaN なら false を返す。

つまり例外なく最初に落とされます。大小比較も同じで、NaN が入ると全部 false になります。

example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
-1 true 1 true
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
true true false

<><=全部 falseになります。「どちらでもない」が正しい答えなので、 否定しても真になりません

ところが includes や Set では等しく扱われる

同じ NaN なのに、結果が変わります。

example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
true true false
javascript
console.log(isNaN('あ'));
console.log(isNaN(undefined));
console.log(isNaN(''));
console.log(isNaN([]));
true true false false

比べ方が3種類あるからです。

比べ方 使っているところ NaNNaN +0-0
IsStrictlyEqual=== === / indexOf false true
SameValueZero includes / Set / Map true true
SameValue Object.is true false
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
number number false
example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
true false

NaN-0どちらを区別したいかで、使う道具が変わります。

  • 素直に比べたい … ===
  • 「その値が入っているか」を見たい … includes / SetNaN も見つかる)
  • -0 まで見分けたい … Object.is

判定するには

=== で判定できないので、専用の関数を使います。

example.js
出力書き換えて実行できます
Ctrl+Enter でも実行JS · UTF-8
NaN NaN NaN NaN NaN

グローバルの isNaN() を使わない

名前がよく似ていますが別物です。isNaN()先に数値へ変換します

'あ'NaN ではありません。変換したら NaN になっただけです。 ''[]false なのも同じ理由で、どちらも 0 に変換されます。

「これは NaN か」を知りたいなら Number.isNaN() を使ってください。 isNaN() が答えているのは「これは数値に変換できないか」という別の質問です。

よくある誤解

typeof NaN は 'number' ではない、と思っている

NaN数値型の値です。「数値ではない(Not a Number)」という名前ですが、型は数値です。

型で弾くことはできません。値として判定する必要があります。

自分自身と等しくないことを利用する

NaN自分自身と等しくない唯一の値です。この性質で判定する古い書き方があります。

たしかに NaN のときだけ true になります。ですが読んだ人が意図を読み取れませんNumber.isNaN(x) と書いてください。

どこで NaN が生まれるか

意図せず NaN になる場面はだいたい決まっています。

数値に変換できない文字列、undefined との計算、定義域の外。 入り口で弾くのがいちばん確実です。 文字列を安全に数値へ直す手順は 文字列を数値に変換する にまとめてあります。

同じ IEEE 754 が決めている小数の誤差については なぜ 0.1 + 0.2 は 0.30000000000000004 になるのか にあります。

配列から NaN を含む重複を消したいときの注意は 配列から重複を取り除く にまとめてあります。

この記事の根拠

  1. IsStrictlyEqual — ECMAScript® 2026 Language Specificationtc39.es
  2. SameValueZero — ECMAScript® 2026 Language Specificationtc39.es
  3. Number.isNaN — ECMAScript® 2026 Language Specificationtc39.es
  4. IEEE 754-2019 — IEEE Standard for Floating-Point Arithmeticstandards.ieee.org

掲載しているコード例は、公開前に読み手が押したときに動くのと同じもので実行して出力を突き合わせています。結果はリポジトリに残しています。

このページの原文(Markdown)