プログラムで数値を扱っていると、いつの間にか計算結果が謎の値になっていることがあります。
その正体の多くは NaN(Not a Number) です。
NaN はエラーではなく「計算できない」という状態を数値型のまま表す特別値です。
本稿では、初心者がハマりがちな比較の罠と、各言語での安全な判定・対処法をわかりやすく解説します。
NaN (Not a Number) の基本
NaN は数値型の特別値
NaN は「数値ではない」を意味しますが、型としては数値型(主に浮動小数点数)の仲間です。
文字列でもエラーでもなく、数値型の中に用意された特別な値だと捉えてください。
多くの演算は、途中のどこかに NaN が混ざると結果も NaN になります(これを「伝播」といいます)。
よくある誤解をほどく
- “NaN” という文字列と NaN は別物です。文字としての “NaN” はあくまで文字列であり、数値としての NaN ではありません。
- null(JavaScript)/None(Python) などの「値がない」と NaN の「計算できない」は役割が違います。NaN は数値型で表す「不明・不定の数値」です。
エラーの代わりに「計算できない」を示す
多くの言語やライブラリは、不適切な数値演算や変換に対して例外を投げる代わりに NaN を返すことがあります。
これによりプログラムを止めずに処理を続けられますが、気づかないまま NaN が混入し、後段のロジックで想定外の動きを招くことがあります。
例(言語ごとのざっくり像)
- JavaScript:
0/0やMath.sqrt(-1)は NaN になります。 - Python:
float('nan')で NaN を作れます。math.sqrt(-1)は例外になりますが、NaN がデータに混じることは珍しくありません(外部入力や科学計算ライブラリ経由など)。 - Java/C#: 浮動小数点演算で不定な結果は NaN になり得ます。
NaN が発生する主なケース
無効な数値変換で NaN
文字列や外部データを数値に変換するとき、変換できない内容だと NaN になります。
- JavaScript:
Number("abc")は NaN、parseFloat("12x")は 12 ですがparseFloat("x12")は NaN です。 - Python:
float("nan")は NaN を作りますが、float("abc")は例外です(この違いが混乱の元です)。
注意
空文字や空白の扱いは関数や言語ごとに異なります。
例えば JavaScript の Number("") は 0 になりますが、parseFloat("") は NaN です。
入力検証は必ず行い、曖昧な変換に頼らないようにしましょう。
不適切な計算で NaN
数学的に定義できない、または浮動小数点として扱いづらい演算の結果は NaN になります。
0/0sqrt(-1)(実数の範囲)Infinity - Infinity(無限大の差)- 負数の対数
log(-1)(実数の範囲)
欠損データや外部入力で NaN
ファイル(CSV/Excel)、センサー、API などから取り込んだデータに、欠損値の印として NaN が入っていることがあります。
データ解析用ライブラリでは、欠損を NaN で表すのが一般的です。
null/undefined/None と NaN の違い
- null/undefined/None: 「値がない/未設定」
- NaN: 「値は数値だが意味のある数ではない(計算不能)」
NaN は伝播する
中間計算に NaN が1つでも入ると、多くの場合その後の計算結果も NaN になります。
早めに検出して取り除くか、妥当な値へ置き換えることが重要です。
NaN 比較の落とし穴
NaN は NaN と等しくない
NaN は自分自身とも等しくありません。
例えば JavaScript の NaN === NaN は false、Python の float('nan') == float('nan') も False です。
これは仕様です。
覚え方
「x が NaN なら x == x は偽」。
逆に言えば、x != x が真なら x は NaN と判断できます(実務では後述の専用関数を使う方が安全です)。
< や > の大小比較が当てにならない
NaN を含む大小比較はすべて偽になる、というのが基本です。
x < 3、x > 3、x <= 3、x >= 3 は、x が NaN のときいずれも偽になります。
そのため条件分岐で取りこぼしが起きます。
検索や重複チェックで見つからないことがある
配列やコレクションの検索・重複判定は、多くの場合「等価比較」に依存します。
NaN は NaN と等しくないため、期待通りヒットしないことがあります。
言語別の落とし穴
- JavaScript:
indexOfは===相当で比較するため、[NaN].indexOf(NaN)は-1です。一方includesは SameValueZero という規則で比較するため、[NaN].includes(NaN)はtrueになります。 - Python:
nan in [float('nan')]はFalseです(等価比較の結果が偽になるため)。集合setや辞書dictに複数の NaN が共存できてしまう点にも注意が必要です。
ソートや条件分岐が意図通りに動かない
ソートでは比較関数内で a < b や a - b を使うことが多いですが、NaN が混ざると比較が常に偽になったり NaN を返してしまい、順序が不定になります。
条件分岐でも if (x > 0) のような判定に NaN が来ると、ブロックに入らず意図せぬスキップが発生します。
実務の指針
ソート・条件分岐・最大最小の計算の前に、必ず NaN を除外または置き換えしてください。
NaN の判定と対処法
等価比較では判定しない
x === NaN(JavaScript) や x == float('nan')(Python) では判定できません。
NaN は等価比較で探さない、が鉄則です。
専用の関数で判定する
各言語が用意する「NaN かどうか」を判定する関数を使います。
これが最も確実で読みやすいです。
代表的な API 一覧
| 言語 | 判定関数 | 型変換の有無 | メモ |
|---|---|---|---|
| JavaScript | Number.isNaN(x) | なし | 数値の NaN だけを真にします。推奨。 |
| JavaScript | isNaN(x) | あり | 入力を数値に変換してから判定。isNaN("abc") は true になります。 |
| Python | math.isnan(x) | なし | 浮動小数点の NaN を判定。配列には numpy.isnan や pandas.isna を使用。 |
| Java | Double.isNaN(x), Float.isNaN(x) | なし | プリミティブの NaN を判定。 |
| C# | double.IsNaN(x), float.IsNaN(x) | なし | .NET の NaN 判定。 |
JavaScript: Number.isNaN と isNaN の使い分け
- 基本は
Number.isNaN(x)を使うと覚えましょう。Number.isNaN("abc")はfalseです。 - 入力が文字列などの可能性が高い場合は、
const n = Number(value); Number.isNaN(n)のように明示変換してから判定します。 isNaN(x)は"abc"のような非数値もtrueになりやすく、判定範囲が広すぎて混乱の元です。
Number.isNaN(NaN)はtrueisNaN("abc")はtrue、Number.isNaN("abc")はfalseNumber.isNaN(Number("abc"))はtrue
Python: math.isnan で NaN 判定
math.isnan(x)を使います。x != xというテクニックもありますが、読みやすさと誤判定防止のため専用関数を使いましょう。- 配列や配列ライブラリでは、
numpy.isnan(arr)、pandas.isna(series_or_df)を使うと便利です。
math.isnan(float('nan'))はTruefloat('nan') == float('nan')はFalse(等価比較では検出できない)
Java/C#: Double.isNaN/double.IsNaN を使う
- Java:
Double.isNaN(x)またはFloat.isNaN(x)を使います。x == Double.NaNは常にfalseです。 - C#:
double.IsNaN(x)またはfloat.IsNaN(x)を使います。x == double.NaNはfalseです。
比較や計算の前に NaN を除外する
最大値・最小値・平均・ソート・しきい値判定などの前に、NaN を取り除くか、末尾に寄せるルールを定めます。
- JavaScript のソート: 比較関数で
Number.isNaN(a)やNumber.isNaN(b)を先に判定し、NaN を常に末尾へ送る。 - Python の統計:
math.isnan(x)でフィルタしてからmin/max/sum/meanを取る。statisticsモジュールは NaN が混ざると結果も NaN になり得ます。
入力を検証し、安全にパースする
「数値として妥当か」を文字列の段階で検証し、曖昧な自動変換に頼らないのが安全です。
具体策
- 事前に空文字や空白のみを弾く。トリムして長さを確認する。
- 許可する書式(整数のみ、小数点1つ、先頭符号のみなど)を正規表現で検証する。
- 変換後は
Number.isFinite(n)(JavaScript) やmath.isfinite(n)(Python) で有限数かどうかまで確認する。
JavaScript の安全パース例(考え方)
- 文字列
sに対し、/^[+-]?(\d+(\.\d+)?|\.\d+)$/のようなパターンで検証してからNumber(s)。 - 変換後に
Number.isFinite(n)で最終チェック。ダメなら NaN とみなして扱いを分ける。
必要ならデフォルト値で置き換える
要件によっては、NaN を 0 や直近値などのデフォルトに置き換えることがあります。
集計や表示を止めないための現実的な手段です。
置き換えの例
- JavaScript:
x = Number.isNaN(x) ? 0 : x - Python:
x = 0 if math.isnan(x) else x
ただし、安易な置き換えは不具合を隠します。
ログに記録する、件数をカウントするなど、検出と可視化も合わせて行いましょう。
まとめ
NaN は「数値型のまま表す計算不能」を示す特別値です。
NaN は NaN と等しくない、大小比較がすべて偽になる、検索やソートで意図通りに扱えないといった性質が、初学者の大きな落とし穴になります。
対策はシンプルで、専用の判定関数を使い、比較や計算の前に NaN を除外・置き換えし、入力の段階で妥当性を検証することです。
この記事のポイントを押さえておけば、NaN による不具合を早期に発見し、安心して数値処理を進められます。
