シナリオ
数値計算の多いコードが正常に動いていたのに、突然動かなくなる。機械学習の損失関数、金融モデル、統計計算など。こんなエラーで処理が落ちます:
OverflowError: math range error
10回中9回は、こういったコードが原因です:
import math
math.exp(1000)
# OverflowError: math range error
あるいは、長い計算の3層奥深くに埋まっていて、途中の計算結果がこっそり巨大な値になっていることもあります。
なぜ起こるのか
PythonのmathモジュールはCのdouble型をラップしています。これは64ビット浮動小数点数で、上限は約1.8 × 10^308です。math.exp(709)は〜8.2×10^307を返し、範囲内に収まります。math.exp(710)は〜2.2×10^308になり、上限を超えます。Pythonはinfを黙って返すのではなく、OverflowError: math range errorを発生させます。
よくある原因:
x > ~709の場合のmath.exp(x)- 大きな入力値を使った
math.cosh()、math.sinh() - 大きな浮動小数点数を大きな冪乗にする場合
- 反復アルゴリズムの途中の値(softmax、log-sum-expなど)
import math
math.exp(710) # OverflowError
math.cosh(800) # OverflowError
10.0 ** 309 # OverflowError
簡易修正:math.inf を使うか入力値をクランプする
クラッシュを止めたくて、infが許容できる結果なら、例外をキャッチしましょう:
import math
def safe_exp(x):
try:
return math.exp(x)
except OverflowError:
return math.inf
print(safe_exp(1000)) # inf
または、expに渡す前に入力値をクランプします:
import math
MAX_EXP = 709.78 # math.exp(709.78) は float64 の上限ギリギリ
def safe_exp(x):
return math.exp(min(x, MAX_EXP))
print(safe_exp(1000)) # 8.218407461554972e+307
根本的な修正:numpy または decimal に切り替える
オプション1 — numpy(数値計算で最もよく使われる)
NumPyのnp.exp()は例外を発生させず、静かにinfを返します:
import numpy as np
np.exp(1000) # inf (エラーなし)
np.exp(710) # inf
np.exp(700) # 1.0142320547350045e+304
オーバーフローの警告も抑制したい場合は、errstateを使います:
import numpy as np
with np.errstate(over='ignore'):
result = np.exp(np.array([700, 800, 1000]))
print(result) # [1.01423205e+304 inf inf]
オプション2 — 任意精度演算には mpmath
infでは不十分で、実際の数値が必要な場合もあります。mpmathは任意の大きさの値を扱えます:
from mpmath import mp, exp
mp.dps = 50 # 小数点以下50桁の精度
result = exp(1000)
print(result)
# 5.0751411135066437985915844766989085095016671145491e+434
オプション3 — Pythonの標準 decimal モジュール
外部ライブラリは不要です。標準のdecimalモジュールでも対応できます:
from decimal import Decimal, getcontext
getcontext().prec = 50
result = Decimal(1000).exp()
print(result)
# 5.075141113506643798591584476698908509501667114549E+434
特殊ケース:Softmax と Log-Sum-Exp のオーバーフロー
softmaxをゼロから実装していませんか?このエラーが最も頻繁に起きる場面です:
import math
def softmax_naive(scores):
exps = [math.exp(s) for s in scores] # s > 709 の場合 OverflowError
total = sum(exps)
return [e / total for e in exps]
softmax_naive([1000, 2000, 3000]) # OverflowError
指数計算の前に最大値を引きましょう。数値安定性のためのよく知られたテクニックで、数学的には同じ結果になりますが、数値が扱いやすい範囲に収まります:
import math
def softmax_stable(scores):
m = max(scores)
exps = [math.exp(s - m) for s in scores]
total = sum(exps)
return [e / total for e in exps]
print(softmax_stable([1000, 2000, 3000]))
# [0.0, 0.0, 1.0] — エラーなし
同じ考え方がlog-sum-expにも適用できます:
import math
def logsumexp(values):
m = max(values)
return m + math.log(sum(math.exp(v - m) for v in values))
print(logsumexp([1000, 2000, 3000])) # 3000.0 — エラーなし
浮動小数点数の上限を調べる
お使いのシステムで正確な上限を知りたいですか?2行で全てわかります:
import sys
print(sys.float_info.max) # 1.7976931348623157e+308
print(sys.float_info.min) # 2.2250738585072014e-308
import math
print(math.log(sys.float_info.max)) # 709.782... — これが exp() の上限
動作確認
修正を適用した後、以下を実行して元のクラッシュが解消されたか確認しましょう:
import math
import numpy as np
# 修正前:
try:
math.exp(1000)
except OverflowError as e:
print(f"まだ壊れています: {e}")
# numpyで修正後:
result = np.exp(1000)
print(f"numpyの結果: {result}") # numpy result: inf
print(f"infかどうか: {np.isinf(result)}") # Is inf: True
# 安定版softmaxで修正後:
scores = [1000, 2000, 3000]
result = softmax_stable(scores)
print(f"Softmax OK: {result}") # [0.0, 0.0, 1.0]
クイックリファレンス
- math.exp() の安全な最大入力値:〜709.78
- エラーの代わりに inf が欲しい場合:
numpy.exp()を使う - 正確な大きな値が必要な場合:
mpmath.exp()またはdecimal.Decimal.exp()を使う - Softmax/log-sum-exp のオーバーフロー:指数計算の前に最大値を引く — 必ず
- numpyのオーバーフロー警告を抑制する:
np.errstate(over='ignore')

