閉じる

NaN (Not a Number) とは?比較でハマる落とし穴と対処法を解説

プログラムで数値を扱っていると、いつの間にか計算結果が謎の値になっていることがあります。

その正体の多くは 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/0Math.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/0
  • sqrt(-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 === NaNfalse、Python の float('nan') == float('nan')False です。

これは仕様です。

覚え方

「x が NaN なら x == x は偽」

逆に言えば、x != x が真なら x は NaN と判断できます(実務では後述の専用関数を使う方が安全です)。

< や > の大小比較が当てにならない

NaN を含む大小比較はすべて偽になる、というのが基本です。

x < 3x > 3x <= 3x >= 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 < ba - b を使うことが多いですが、NaN が混ざると比較が常に偽になったり NaN を返してしまい、順序が不定になります。

条件分岐でも if (x > 0) のような判定に NaN が来ると、ブロックに入らず意図せぬスキップが発生します。

実務の指針

ソート・条件分岐・最大最小の計算の前に、必ず NaN を除外または置き換えしてください。

NaN の判定と対処法

等価比較では判定しない

x === NaN(JavaScript) や x == float('nan')(Python) では判定できません。

NaN は等価比較で探さない、が鉄則です。

専用の関数で判定する

各言語が用意する「NaN かどうか」を判定する関数を使います

これが最も確実で読みやすいです。

代表的な API 一覧

言語判定関数型変換の有無メモ
JavaScriptNumber.isNaN(x)なし数値の NaN だけを真にします。推奨。
JavaScriptisNaN(x)あり入力を数値に変換してから判定。isNaN("abc")true になります。
Pythonmath.isnan(x)なし浮動小数点の NaN を判定。配列には numpy.isnanpandas.isna を使用。
JavaDouble.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)true
  • isNaN("abc")trueNumber.isNaN("abc")false
  • Number.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'))True
  • float('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.NaNfalse です。

比較や計算の前に 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 による不具合を早期に発見し、安心して数値処理を進められます。

この記事を書いた人
エーテリア編集部
エーテリア編集部

このサイトでは、プログラミングをこれから学びたい初心者の方に向けて記事を書いています。 基本的な用語や環境構築の手順から、実際に手を動かして学べるサンプルコードまで、わかりやすく整理することを心がけています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!