数値計算の誤差とは?丸め誤差・打切り誤差・桁落ち・情報落ちの原因と対策

プログラミングやデータ分析において、計算結果が期待と異なる経験はありませんか?その原因の多くは「数値計算の誤差」にあります。本記事では、コンピュータで数値計算を行う際に必ず理解しておくべき4つの誤差(丸め誤差、打切り誤差、桁落ち、情報落ち)について、初心者にもわかりやすく徹底解説します。

テックジム東京本校では、情報科目の受験対策指導もご用意しております。

数値計算における誤差とは

誤差とは、真の値と計算結果との差のことです。コンピュータは有限のメモリで数値を表現するため、完全に正確な計算ができない場合があります。これらの誤差を理解することで、より精度の高いプログラムを作成できます。

誤差が発生する主な原因

  • コンピュータの数値表現の限界
  • 計算アルゴリズムの特性
  • 演算の順序や方法

それでは、4つの代表的な誤差について詳しく見ていきましょう。

1. 丸め誤差(Rounding Error)

丸め誤差とは

丸め誤差は、コンピュータが数値を表現する際の桁数に限界があるために発生する誤差です。無限に続く小数や非常に大きな数を有限の桁数で表現する際に生じます。

発生する仕組み

コンピュータは浮動小数点数を使って実数を表現しますが、表現できる桁数には限界があります。例えば、倍精度浮動小数点数(double型)では約15〜16桁の精度しかありません。

具体例

# 0.1を3回足す計算
result = 0.1 + 0.1 + 0.1
print(result)  # 0.30000000000000004 と表示される(期待値は0.3)

この例では、0.1が2進数で正確に表現できないため、丸め誤差が蓄積されます。

丸め誤差の対策

  1. 高精度演算ライブラリの使用: Pythonのdecimalモジュールなど
  2. 誤差を考慮した等価判定: 完全一致ではなく、許容範囲内での比較
  3. スケーリング: 整数演算に変換してから計算
from decimal import Decimal

# Decimalモジュールを使用した高精度計算
result = Decimal('0.1') + Decimal('0.1') + Decimal('0.1')
print(result)  # 0.3 と正確に表示される

2. 打切り誤差(Truncation Error)

打切り誤差とは

打切り誤差は、無限に続く計算や級数展開を有限の項で打ち切ることによって生じる誤差です。理論的には無限回の計算が必要な処理を、現実的な回数で終了させることで発生します。

発生する仕組み

数学的には無限級数や極限として定義される値を、コンピュータでは有限回の計算で近似する必要があります。この打ち切りによって理論値との差が生まれます。

具体例

テイラー展開による近似計算

e^x(自然対数の底のx乗)をテイラー展開で計算する場合:

e^x = 1 + x + x²/2! + x³/3! + x⁴/4! + …

この級数を10項で打ち切れば、それ以降の項による誤差が打切り誤差となります。

import math

def exp_approximation(x, n_terms):
    """テイラー展開によるe^xの近似(n項まで)"""
    result = 0
    for n in range(n_terms):
        result += (x ** n) / math.factorial(n)
    return result

x = 1.0
print(f"10項での近似: {exp_approximation(x, 10)}")
print(f"20項での近似: {exp_approximation(x, 20)}")
print(f"正確な値: {math.exp(x)}")

打切り誤差の対策

  1. 計算項数の増加: より多くの項を計算に含める
  2. 収束判定の導入: 誤差が許容範囲内になったら計算を終了
  3. 高次の近似手法の採用: より速く収束する計算方法を選択
  4. 適応的手法の利用: 必要に応じて計算精度を調整

3. 桁落ち(Cancellation Error / Loss of Significance)

桁落ちとは

桁落ちは、非常に近い値同士を引き算したときに、有効数字が大幅に減少する現象です。数値計算における最も深刻な誤差の一つとされています。

発生する仕組み

近い値同士の減算を行うと、上位の桁が相殺され、下位の桁(丸め誤差を含む)が結果の上位桁になってしまいます。これにより、相対誤差が急激に増大します。

具体例

二次方程式の解の公式での桁落ち

ax² + bx + c = 0 の解は、x = (-b ± √(b² – 4ac)) / 2a

b² ≫ 4ac のとき、√(b² – 4ac) ≈ |b| となり、-b と √(b² – 4ac) の減算で桁落ちが発生します。

import math

# 桁落ちが発生する例
a = 1.0
b = 10000.0
c = 1.0

# 通常の解の公式(桁落ちが発生)
discriminant = math.sqrt(b**2 - 4*a*c)
x1 = (-b + discriminant) / (2*a)
x2 = (-b - discriminant) / (2*a)

print(f"通常の公式: x1={x1}, x2={x2}")

# 改良版(桁落ちを回避)
if b > 0:
    x1_improved = (-b - discriminant) / (2*a)
    x2_improved = c / (a * x1_improved)
else:
    x1_improved = (-b + discriminant) / (2*a)
    x2_improved = c / (a * x1_improved)

print(f"改良版: x1={x1_improved}, x2={x2_improved}")

桁落ちの対策

  1. 数式の変形: 減算を避けるように式を変形
  2. 計算順序の工夫: 絶対値の大きい数から計算
  3. 等価な式の利用: 有理化などの数学的テクニック
  4. 精度の向上: より高精度のデータ型を使用

数式変形の例

  • (a – b) の代わりに (a² – b²)/(a + b) を使用
  • √(1 + x) – 1 の代わりに x/(√(1 + x) + 1) を使用

4. 情報落ち(Absorption Error)

情報落ちとは

情報落ちは、絶対値が大きく異なる数同士を加減算した際に、小さい方の数の情報が失われる現象です。

発生する仕組み

コンピュータの浮動小数点数は、指数部と仮数部で数値を表現します。絶対値の差が非常に大きい数同士を演算すると、小さい数が表現可能な桁数を超えてしまい、計算結果に反映されなくなります。

具体例

# 情報落ちの例
large_number = 1.0e20
small_number = 1.0

# 大きな数に小さな数を加算
result = large_number + small_number
print(f"結果: {result}")
print(f"元の値と同じか: {result == large_number}")  # True

# 小さい数の情報が完全に失われている
print(f"差: {result - large_number}")  # 0.0 が表示される

累積計算での情報落ち

# 順番によって結果が変わる例
numbers = [1.0e20, 1.0, 1.0, 1.0, -1.0e20]

# 左から順に計算
result1 = sum(numbers)
print(f"左から計算: {result1}")

# 絶対値の小さいものから計算
sorted_numbers = sorted(numbers, key=abs)
result2 = sum(sorted_numbers)
print(f"小さい順から計算: {result2}")

情報落ちの対策

  1. 計算順序の最適化: 絶対値の小さい数から順に加算
  2. 補償加算法の使用: Kahan summation algorithmなどの高精度加算アルゴリズム
  3. スケーリング: 数値の大きさを揃えてから計算
  4. データの前処理: 平均値を引くなどの正規化

Kahan summation algorithmの実装例

def kahan_sum(numbers):
    """補償加算法による高精度合計計算"""
    total = 0.0
    compensation = 0.0  # 失われた小さな値を補償
    
    for number in numbers:
        y = number - compensation
        temp = total + y
        compensation = (temp - total) - y
        total = temp
    
    return total

# テスト
test_numbers = [1.0e20, 1.0, 2.0, 3.0, -1.0e20]
print(f"通常の合計: {sum(test_numbers)}")
print(f"Kahan法: {kahan_sum(test_numbers)}")

誤差の種類別比較表

誤差の種類 原因 発生タイミング 深刻度 主な対策
丸め誤差 有限桁数での表現 数値の保存時 高精度型の使用
打切り誤差 無限計算の有限化 アルゴリズム実行時 項数の増加
桁落ち 近似値同士の減算 演算時 数式の変形
情報落ち 大小の差が大きい加減算 演算時 中〜高 計算順序の工夫

実践的なプログラミングでの注意点

1. 浮動小数点数の比較

# 悪い例
if a == b:
    print("等しい")

# 良い例
epsilon = 1e-10
if abs(a - b) < epsilon:
    print("ほぼ等しい")

2. ループでの累積計算

# 誤差が蓄積する例
total = 0.0
for i in range(1000000):
    total += 0.0001

print(total)  # 100にならない可能性がある

# 改善例:整数で計算
count = 0
for i in range(1000000):
    count += 1

total = count * 0.0001
print(total)  # より正確

3. 数値の正規化

# 大きな値を扱う前に正規化
data = [1.0e10, 1.0e10 + 1, 1.0e10 + 2]

# 平均値を引く
mean = sum(data) / len(data)
normalized_data = [x - mean for x in data]

# 正規化したデータで計算
result = sum(normalized_data)  # より精度が高い

まとめ

数値計算における4つの主要な誤差について解説しました。

重要なポイント

  1. 丸め誤差: コンピュータの数値表現の限界による誤差。高精度型の使用で対処。
  2. 打切り誤差: 無限計算を有限で打ち切ることによる誤差。計算項数の調整で対処。
  3. 桁落ち: 近い値の減算で発生する深刻な誤差。数式の変形で回避が必須。
  4. 情報落ち: 大小の差がある数の加減算で発生。計算順序の工夫で対処。

実務での心得

  • 浮動小数点数の厳密な比較は避ける
  • 可能な限り整数演算を使用する
  • アルゴリズムの数値的安定性を考慮する
  • 必要に応じて高精度演算ライブラリを活用する
  • 計算結果の妥当性を常に検証する

これらの誤差を理解し適切に対処することで、より信頼性の高い数値計算プログラムを開発することができます。科学技術計算、金融計算、機械学習など、精度が重要な分野では特に注意が必要です。

参考資料

数値計算についてさらに学びたい方は、以下のトピックも調べてみてください:

  • IEEE 754浮動小数点数標準
  • 数値線形代数における条件数
  • 数値積分と数値微分の誤差
  • 誤差伝播の理論
  • 数値計算の安定性解析

正確な数値計算は、現代のデータサイエンスとプログラミングにおいて不可欠なスキルです。本記事が、より精度の高いプログラム開発の一助となれば幸いです。

らくらくPython塾 – 読むだけでマスター

【現役エンジニア歓迎】プログラミング学習お悩み相談会

【情報I】受験対策・お悩み相談会(オンライン・無料)

【オンライン無料】ゼロから始めるPython爆速講座

テックジム東京本校

格安のプログラミングスクールといえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
対面型でより早くスキル獲得、月額2万円のプログラミングスクールです。
情報科目の受験対策指導もご用意しております。