NaN と Inf の正しい扱い方(math.isnan, math.isinf)|Python
浮動小数点数には通常の数値以外に、特殊な値として NaN(Not a Number)と Inf(Infinity)が存在する。これらは IEEE 754 規格で定義されており、数学的に未定義な演算や極限を表現する。正しく扱わないとバグの温床になるため、その性質を理解しておくことが重要だ。
Inf(無限大)の発生
Inf はゼロ除算や、表現可能な範囲を超えた計算で発生する。
import math
# ゼロ除算(正の無限大)
print(1.0 / 0.0) # inf
# 負のゼロ除算(負の無限大)
print(-1.0 / 0.0) # -inf
# オーバーフロー
print(1e308 * 10) # inf
# math モジュールで生成
print(math.inf) # inf
print(-math.inf) # -inffloat('inf') や float('-inf') でも無限大を作成できる。
NaN(非数)の発生
NaN は数学的に定義されない演算の結果として発生する。
import math
# 0 / 0 は不定形
print(0.0 / 0.0) # nan
# 無限大同士の減算
print(math.inf - math.inf) # nan
# 負の数の平方根(実数の範囲では定義されない)
print(math.sqrt(-1)) # ValueError(math は例外を投げる)
# float で NaN を生成
print(float('nan')) # nan
print(math.nan) # nanmath.sqrt(-1) は ValueError を投げるが、NumPy の np.sqrt(-1) は nan を返す。ライブラリによって挙動が異なる点に注意。
NaN の特殊な性質
NaN には驚くべき性質がある。自分自身と等しくない。
import math
x = float('nan')
print(x == x) # False(NaN は自分自身と等しくない)
print(x != x) # Trueこの性質を利用して x != x で NaN を判定できるが、可読性の観点から math.isnan() を使うべきだ。
import math
x = float('nan')
print(math.isnan(x)) # True
y = 3.14
print(math.isnan(y)) # FalseInf の判定
math.isinf() で無限大かどうかを判定できる。正負どちらの無限大でも True を返す。
import math
print(math.isinf(math.inf)) # True
print(math.isinf(-math.inf)) # True
print(math.isinf(1e308)) # False(大きいが有限)正の無限大か負の無限大かを区別したい場合は、値そのものを比較する。
import math
x = float('-inf')
if math.isinf(x):
if x > 0:
print('正の無限大')
else:
print('負の無限大') # こちらが出力されるmath.isfinite() で有限値を判定
math.isfinite() は NaN でも Inf でもない通常の有限値のときに True を返す。
import math
print(math.isfinite(3.14)) # True
print(math.isfinite(math.inf)) # False
print(math.isfinite(math.nan)) # Falseデータの検証によく使われる。
x が NaN のとき True。未定義の計算結果を検出する。
x が ±Inf のとき True。オーバーフローや極限を検出する。
NaN の伝播
NaN を含む演算の結果は NaN になる。これを「NaN の伝播」と呼ぶ。
import math
x = float('nan')
print(x + 1) # nan
print(x * 100) # nan
print(x < 5) # False
print(x > 5) # False
print(x == 5) # False比較演算はすべて False を返す(!= を除く)。このため、ソート時に NaN が含まれると予期しない結果になることがある。
実用例:データのクレンジング
外部データを処理する際、欠損値や異常値が NaN や Inf として表れることがある。
import math
def clean_data(values):
"""有限値のみを抽出する"""
return [v for v in values if math.isfinite(v)]
raw = [1.0, 2.5, float('nan'), 3.7, float('inf'), -1.2]
cleaned = clean_data(raw)
print(cleaned) # [1.0, 2.5, 3.7, -1.2]実用例:安全な除算
ゼロ除算を安全に処理する関数を作る。
import math
def safe_divide(a, b, default=0.0):
"""ゼロ除算や無効な結果を default で置き換える"""
if b == 0:
return default
result = a / b
if not math.isfinite(result):
return default
return result
print(safe_divide(10, 2)) # 5.0
print(safe_divide(10, 0)) # 0.0
print(safe_divide(1e309, 1)) # 0.0(オーバーフロー対策)注意点:整数のゼロ除算
整数同士のゼロ除算は ZeroDivisionError を投げる。Inf にはならない。
# 整数のゼロ除算 → 例外
# print(1 / 0) # ZeroDivisionError
# 浮動小数点のゼロ除算 → Inf
print(1.0 / 0.0) # infこの違いを意識しておかないと、予期しない例外でプログラムが停止することがある。












