近い値同士の引き算で有効桁数が大幅に減る現象。1.000001 - 1.000000 のような計算で精度が失われます。
桁落ちとは、ほぼ同じ値同士を引き算したとき、有効桁数が激減する現象です。計算自体は正しいのに、結果の信頼性がガタ落ちするという厄介な問題です。
日常の例で考えてみましょう。あなたの体重計は10g単位で測れますが、最後の1桁はやや不正確です。
・朝の体重:65.32 kg(有効桁4桁)
・夜の体重:65.35 kg(有効桁4桁)
・差分:65.35 - 65.32 = 0.03 kg
元の数は4桁の精度がありましたが、差をとると上位の「65.3」が打ち消し合って消え、残ったのは「3」の1桁だけ。しかもこの「3」は体重計の最下位桁(最も不正確な桁)です。実際の差は0.02〜0.04 kgのどこかかもしれません。元の数は4桁の精度があったのに、差をとったら1桁の精度しかない。これが桁落ちです。
コンピュータの浮動小数点数ではこれがもっと深刻になります。有効桁数6桁の数で具体的に見てみましょう。
・A = 1.23456(有効桁6桁)
・B = 1.23451(有効桁6桁)
・A - B = 0.00005
結果の 0.00005 は有効桁数がたった1桁です。上位5桁の「1.2345」が完全に打ち消し合って消えてしまい、残ったのは末尾の「5」だけ。しかもこの「5」は元の数の最下位桁なので、丸め誤差を含んでいる可能性が最も高い桁です。
つまり桁落ちの怖さは、「正確だった上位桁が消え、不正確な下位桁だけが残る」ことです。見た目上は0.00005という数値が得られますが、実際には数十パーセントの誤差を含んでいる可能性があります。上のツールでAとBに近い値を入力して、有効桁数がどれだけ減るか試してみてください。
桁落ちは「ほぼ等しい2つの値の引き算」が登場するあらゆる場面で発生します。実務やアルゴリズムでは次のような計算が典型的な要注意箇所です。
・2点間の差分計算:センサー値の前回値と今回値の差、時刻同士の引き算など
・数値微分:(f(x+h) - f(x)) / h は h を小さくすると分子で桁落ち
・二次方程式の解の公式:-b + √(b²-4ac) で b が大きいと両者が近づき桁落ち
・三角関数の差:1 - cos(x) は x が小さいと cos(x) が 1 に近く桁落ち
・√(x+1) - √x:x が大きいと両者がほぼ同じになり桁落ち
仕組みとしては、A と B の上位の桁が同じ場合、引き算すると上位桁が 0 となって消失します。たとえば 1.23456 と 1.23451 の上位5桁「1.2345」はまったく同じなので、引き算するとすべて 0 になって消えてしまい、残るのは下位の桁だけです。 ところが浮動小数点数では下位の桁ほど不正確(丸め誤差を含む可能性が高い)。結果として「正確だった上位桁が消え、不正確な下位桁だけが残る」状態になり、信頼性が極めて低い数値になってしまいます。
上のツールで A と B に近い値を入力すると、グレーの「一致桁」が増えて有効桁数メーターが減る様子を確認できます。スライダーで A と B を徐々に近づけ、どの時点で有効桁数が激減するかを観察してみましょう。
桁落ちは「計算する側がやらかしている」誤差なので、式を書き換えて引き算そのものを回避するのが最大の対策です。精度を上げる(倍精度→四倍精度など)だけでは根本解決になりません。
回避策1:有理化で引き算を消す
代表例は √(x+1) - √x。分母分子に √(x+1) + √x を掛けて1 / (√(x+1) + √x) に変形すれば、足し算しか出てこないので桁落ちしません。
回避策2:三角関数の恒等式で書き換える
1 - cos(x) は x が小さいと桁落ちしますが、半角公式で 2·sin(x/2)² に置き換えると引き算が消えます。
回避策3:二次方程式の解の公式を分けて使う
b が大きいときは -b + √(b²-4ac) で桁落ち。代わりに-2c / (b + √(b²-4ac)) を使うか、解と係数の関係(積 = c/a)から残りの解を求めます。
回避策4:テイラー展開で近似する
差分を直接計算せず、関数の展開式から「引き算が消えた近似式」を導きます。1 - cos(x) ≈ x²/2 など。
桁落ちを防ぐ最も有効な方法は、「式を変形して近い値同士の引き算を避ける」ことです。具体的な対策コード例は次のカード(プログラミングでの注意点)も参照してください。
桁落ちの最も効果的な対策は、数式を数学的に変形して、近い値同士の引き算が現れないようにすることです。「計算の精度を上げる」のではなく、「そもそも桁落ちが起きない式に書き換える」のがポイントです。
対策1: 有理化(最も基本的なテクニック)
例えば x = 10000 のとき sqrt(x+1) - sqrt(x) を計算すると:
・sqrt(10001) = 100.00499987...
・sqrt(10000) = 100.00000000...
・差 = 0.00499987...(上位5桁が打ち消されて桁落ち!)
これを分母分子に sqrt(x+1)+sqrt(x) を掛けて有理化すると:
・sqrt(x+1) - sqrt(x) = 1 / (sqrt(x+1) + sqrt(x))
・= 1 / (100.005 + 100.000) = 1 / 200.005 = 0.00499987...
同じ答えが得られますが、こちらの式には近い値の引き算がどこにも出てこないので、桁落ちが発生しません。
対策2: 同値な別の公式を使う
・1 - cos(x)(xが小さいとき cos(x) が1に近く桁落ち)→ 2 sin(x/2)2 に書き換え
・二次方程式の解の公式で -b + sqrt(b2-4ac)(b が大きいとき桁落ち)→ -2c / (b + sqrt(b2-4ac)) に書き換え
対策3: テイラー展開で近似する
関数の差を展開して、引き算を含まない近似式に変換する方法です。例えば x が十分小さいとき、1 - cos(x) はテイラー展開で x2/2 と近似でき、引き算が消えます。
注意点として、精度を上げる(倍精度→四倍精度など)だけでは根本解決になりません。桁落ちで失われる桁数は変わらず、「もう少し多く桁が残る」だけです。式変形が最優先の対策となります。
| 誤差の種類 | 原因 | 典型例 | 対策 |
|---|---|---|---|
| 桁落ち | 近い値の減算 | 1.23456 - 1.23451 | 式の変形 |
| 情報落ち | 大小差の大きい加減算 | 1億 + 1 | 加算順序の工夫 |
| 丸め誤差 | 有限桁への丸め | 0.1の2進表現 | 精度を上げる |
| 打切り誤差 | 無限級数の打ち切り | sin(x)の近似 | 項数を増やす |
これら4つの誤差は違いを正確に区別することが大切です。 特に「桁落ち」と「情報落ち」は名前が似ているので混同しやすいですが、桁落ちは近い値の引き算、情報落ちは大小差の大きい加減算と整理して覚えましょう。
覚え方のコツ: 桁落ちは「桁が落ちる(減る)」→ 有効桁数が減少。情報落ちは「情報が落ちる(消える)」→ 小さい数の情報が消失。 丸め誤差は「丸める」→ ビット数制限で四捨五入。打切り誤差は「打ち切る」→ 無限級数を途中で止める。名前がそのまま原因のヒントになっています。
実際のプログラミングでは、桁落ちが起こりうる箇所に注意が必要です:
・浮動小数点数の等値比較は避ける: a == b ではなく |a - b| < eps を使う
・二次方程式の解の公式: b^2 と 4ac が近い場合に桁落ちする → 解と係数の関係を使う
・数値微分: f(x+h) - f(x) で桁落ちが起きやすい → 中心差分 (f(x+h) - f(x-h))/(2h) を使う
桁落ちはコンパイラやCPUが自動で防いでくれるものではないため、プログラマが意識して対策する必要があります。