Tình huống
Code tính toán nặng của bạn đang chạy ổn — cho đến khi nó không ổn nữa. Một hàm loss trong machine learning, một mô hình tài chính, một phép tính thống kê. Rồi đột nhiên nổ ra với:
OverflowError: math range error
Chín trên mười trường hợp, nguyên nhân bắt nguồn từ đại loại như thế này:
import math
math.exp(1000)
# OverflowError: math range error
Hoặc nó bị chôn vùi ba tầng sâu trong một phép tính dài hơn, nơi một kết quả trung gian nào đó âm thầm phình to khổng lồ.
Tại sao điều này xảy ra
Module math của Python bọc kiểu double của C — một số thực 64-bit với giới hạn trên xấp xỉ 1.8 × 10^308. math.exp(709) trả về ~8.2×10^307, vẫn nằm trong giới hạn. math.exp(710) sẽ trả về ~2.2×10^308 — và con số đó vượt ngưỡng. Thay vì trả về inf âm thầm, Python ném ra OverflowError: math range error.
Các thủ phạm thường gặp:
math.exp(x)khix > ~709math.cosh(),math.sinh()với đầu vào quá lớn- Lũy thừa một số thực lớn lên một số mũ lớn
- Các giá trị trung gian trong thuật toán lặp (ví dụ: softmax, log-sum-exp)
import math
math.exp(710) # OverflowError
math.cosh(800) # OverflowError
10.0 ** 309 # OverflowError
Sửa nhanh: Dùng math.inf hoặc giới hạn đầu vào
Muốn code không crash nữa và inf là kết quả chấp nhận được? Bắt ngoại lệ:
import math
def safe_exp(x):
try:
return math.exp(x)
except OverflowError:
return math.inf
print(safe_exp(1000)) # inf
Hoặc giới hạn đầu vào trước khi nó chạm đến exp:
import math
MAX_EXP = 709.78 # math.exp(709.78) vừa dưới giới hạn float64
def safe_exp(x):
return math.exp(min(x, MAX_EXP))
print(safe_exp(1000)) # 8.218407461554972e+307
Sửa triệt để: Chuyển sang numpy hoặc decimal
Lựa chọn 1 — numpy (phổ biến nhất cho tính toán số)
np.exp() của NumPy trả về inf âm thầm, không ném ngoại lệ:
import numpy as np
np.exp(1000) # inf (không có lỗi)
np.exp(710) # inf
np.exp(700) # 1.0142320547350045e+304
Muốn tắt luôn cả cảnh báo overflow? Dùng 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]
Lựa chọn 2 — mpmath để có độ chính xác tùy ý
Đôi khi inf chưa đủ — bạn cần con số thực sự. mpmath xử lý được các giá trị lớn tùy ý:
from mpmath import mp, exp
mp.dps = 50 # 50 chữ số thập phân độ chính xác
result = exp(1000)
print(result)
# 5.0751411135066437985915844766989085095016671145491e+434
Lựa chọn 3 — module decimal có sẵn trong Python
Không cần thư viện ngoài. Module decimal chuẩn cũng xử lý được:
from decimal import Decimal, getcontext
getcontext().prec = 50
result = Decimal(1000).exp()
print(result)
# 5.075141113506643798591584476698908509501667114549E+434
Trường hợp đặc biệt: Tràn số trong Softmax và Log-Sum-Exp
Tự cài softmax từ đầu? Đây là nơi lỗi hay cắn nhất:
import math
def softmax_naive(scores):
exps = [math.exp(s) for s in scores] # OverflowError nếu s > 709
total = sum(exps)
return [e / total for e in exps]
softmax_naive([1000, 2000, 3000]) # OverflowError
Trừ giá trị lớn nhất trước khi tính hàm mũ. Đây là thủ thuật ổn định số học nổi tiếng — kết quả toán học hoàn toàn tương đương, nhưng các con số vẫn trong tầm kiểm soát:
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] — không có lỗi
Tương tự với 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 — không có lỗi
Kiểm tra giới hạn số thực
Tò mò giới hạn trên nằm chính xác ở đâu trên hệ thống của bạn? Hai dòng này cho bạn biết tất cả:
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... — đây là giới hạn trên của exp()
Kiểm tra lại
Chạy đoạn này sau khi áp dụng cách sửa để xác nhận lỗi gốc đã biến mất:
import math
import numpy as np
# Trước khi sửa:
try:
math.exp(1000)
except OverflowError as e:
print(f"Vẫn còn lỗi: {e}")
# Sau khi sửa bằng numpy:
result = np.exp(1000)
print(f"Kết quả numpy: {result}") # numpy result: inf
print(f"Là inf: {np.isinf(result)}") # Is inf: True
# Sau khi sửa bằng softmax ổn định:
scores = [1000, 2000, 3000]
result = softmax_stable(scores)
print(f"Softmax OK: {result}") # [0.0, 0.0, 1.0]
Tham khảo nhanh
- Đầu vào an toàn tối đa cho math.exp(): ~709.78
- Muốn trả về inf thay vì lỗi: dùng
numpy.exp() - Cần giá trị lớn chính xác: dùng
mpmath.exp()hoặcdecimal.Decimal.exp() - Tràn số trong softmax/log-sum-exp: luôn trừ giá trị lớn nhất trước khi tính exp
- Tắt cảnh báo overflow của numpy:
np.errstate(over='ignore')

