閉じる

オーバーフローとアンダーフローとは?原因と回避策をやさしく解説

プログラムで使う数字には、表せる範囲があり、限界を超えると結果が壊れます。

本記事では、オーバーフローとアンダーフローの意味、起きる理由、そして初心者でもできる回避策を段階的に説明します。

整数と浮動小数点の違いから、具体例、検出と対処まで、基礎をやさしく押さえていきます。

エンジニアの求人
読み込み中…

数値表現の基本と範囲

ビット数と範囲(上限/下限)の考え方

コンピュータは数値をビット(0と1)の並びで表します。

ビット数が多いほど表せる数の種類が増え、上限と下限の幅が広がります

例えば符号付き整数では、おおまかに-2^(n-1)から2^(n-1)-1までの範囲を持ちます。

符号なし整数の場合は0から2^n-1です。

次の表は、よく使う型の範囲イメージです(概略、言語や環境で異なることがあります)。

型の例ビット数範囲の例(概略)
符号付き整数(int8)8-128 〜 127
符号付き整数(int16)16-32768 〜 32767
符号付き整数(int32)32-2147483648 〜 2147483647
符号付き整数(int64)64約-9.22e18 〜 約9.22e18
浮動小数点(float32)32約±1.18e-38 〜 約±3.4e38
浮動小数点(float64)64約±2.23e-308 〜 約±1.8e308

範囲の端を超えると、オーバーフローやアンダーフローが起きます

この後で具体的に見ていきます。

整数と浮動小数点の違い

整数型は、範囲内であれば整数値を正確に表します。

対して浮動小数点型は、小数を含む広い範囲を表せますが、一部の数を近い値に丸めて表現します。

例えば0.1は2進数で有限桁にならないため、正確には表せません。

さらに浮動小数点には、無限大(Infinity)や非数(NaN)といった特別な値もあります。

丸めの影響と誤差のイメージ

丸めは保存時や計算中にも起こります。

代表例として、次の結果は多くの言語で見られます。

0.1 + 0.2  // 0.30000000000000004 など

これは計算が壊れているのではなく、表現の限界によるごく小さな誤差です。

繰り返し計算で誤差が蓄積したり、ほぼ同じ数の引き算で有効桁が失われたりすることがあるため、丸め誤差は起きうるものとして前提にすることが大切です。

オーバーフローとは?原因と例

定義

オーバーフローとは、計算結果の絶対値がその型で表せる上限を超え、別の値に化けてしまう現象です。

整数では桁が回って全く別の値になります。

浮動小数点ではInfinity(無限大)になることが一般的です。

整数のオーバーフローの例と症状

32ビット整数では最大値は2147483647です。

これに1を足すと、上限を超えて桁が回ります。

C言語
// Cの例
#include <stdio.h>
#include <limits.h>

int main() {
  int x = INT_MAX;   // 2147483647
  int y = x + 1;
  printf("%d\n", y); // -2147483648 に見える(桁回り)
}

桁回りの典型的な症状は、急に負の数に変わる値が小さくなるループの終了条件が壊れるなどです。

符号なし8ビット(0〜255)でも255+1が0になります。

浮動小数点のオーバーフローの例

とても大きな数同士を掛けると、無限大に飛びます。

Python
x = 1e308
y = x * 1e10
print(y)  # inf (無限大)

infが出たらオーバーフローの合図です。

よくある原因

入力や計算結果が想定より大きくなる場面で起きます。

例えば、合計値をひたすら足し続ける大きな数の乗算や累乗型を小さい型へ変換単位のミス(秒とミリ秒の取り違えなど)が原因になりやすいです。

オーバーフローの検出方法

最も確実なのは、演算の前に範囲をチェックすることです。

加算の例を擬似コードで示します。

text
// 符号付き32ビット整数の安全な加算(概念)
if (b > 0 && a > INT_MAX - b) then エラー
if (b < 0 && a < INT_MIN - b) then エラー
結果 = a + b

浮動小数点では計算後にisfiniteisinfで確認します。

Python
import math
z = x * y
if not math.isfinite(z):  # infやnanを含む
    print("範囲外の計算が発生しました")

結果がおかしい兆候(急な符号反転や極端な値)をログに出すのも有効です。

アンダーフローとは?原因と例

定義

アンダーフローとは、結果が0に近すぎて、その型では0として扱われるか、ほぼ0の特別な値になってしまう現象です。

整数の世界では下側の範囲外も「オーバーフロー」と呼ぶことが多く、ここでは主に浮動小数点の話を扱います。

浮動小数点でアンダーフローが起きやすい理由

浮動小数点には扱える最小の大きさがあります。

この最小値より小さい結果は0.0に丸められたり、極端に小さい近似値になったりします

特にとても小さい数の掛け算や、大きい数での割り算を繰り返すと起きやすいです。

Python
x = 1e-308
y = x * 1e-308
print(y)  # 0.0 (doubleの範囲を下回り0に)

よくある原因

確率や正規化で値がどんどん小さくなる非常に大きな数で割る処理指数関数の大きな負の指数などが典型です。

ほぼ同じ数同士の引き算で有効桁が失われ、結果が極端に小さくなることもあります。

影響

本来は小さいけれど0ではない値が0.0に落ちると、ゼロ除算や分岐条件の誤作動につながります。

例えば分母が0になって無限大NaNを生む、しきい値判定がすべて偽になる、といった予期せぬ挙動が発生します。

アンダーフローの検出方法

演算後に不自然な0.0が紛れ込んでいないかを確認します。

Python
import math

tiny = 1e-300  # double向けの一例。用途に応じて調整
if x != 0.0 and y != 0.0:
    z = x * y
    if z == 0.0 or abs(z) < tiny:
        print("アンダーフローの可能性があります")

また、ライブラリによっては非正規化underflowの警告設定が用意されています。

利用している言語やライブラリの「最小正規化値」を調べ、それ未満は0扱いにするか、スケールを変える方針を決めると良いです。

プログラミングの回避策とチェック方法

型選びと範囲の見積もり

最初に、扱う値のおおまかな最大値と最小値を見積もることが重要です。

カウント類はint64、広い桁の小数はfloat64、通貨は誤差を嫌うため10進小数型(Decimalなど)が向く場合があります。

PythonのintやJavaのBigIntegerのような任意精度も選択肢です。

用途の例向くことが多い型
件数やID64ビット整数や任意精度整数
測定値や比率float64(倍精度)
通貨Decimalや整数で最小通貨単位を扱う方式

範囲チェックとガード

演算の前に安全かどうかを判定すると事故が減ります。

加算や乗算は次のようにガードします。

text
// 乗算の一例(概念)
if (a != 0 && abs(b) > MAX / abs(a)) then エラー
結果 = a * b

浮動小数点は計算後にisfiniteで検査し、問題があれば例外やエラーログに回します。

演算順序の工夫とスケーリング

大きすぎる/小さすぎる中間結果を作らない工夫が効きます。

例えばa*b/cは、先にb/cを計算して値を小さくしてからaを掛ける方が安全な場合があります。

全体を一定係数で割ってから計算し、最後に元に戻すスケーリングも有効です。

指数が絡む場合は、対数で扱うとオーバーフローやアンダーフローを避けやすくなります。

大きすぎる/小さすぎる値のクランプ

入力や中間値を一定範囲にクランプ(はさみこみ)して暴走を防げます。

Python
def clamp(x, low, high):
    return max(low, min(x, high))

x = clamp(x, -1e6, 1e6)  # 一例

ただし、クランプは本質的なバグを隠す場合があります

警告ログやカウンタを付けて、発生頻度を監視しましょう。

安全なライブラリの利用

チェック付き算術飽和算術(上限/下限で止める)を提供するライブラリを使うと安全です。

Pythonではdecimalfractions、JavaではBigInteger/BigDecimalなどが代表例です。

数値計算ライブラリでは、エラー処理や警告設定を有効にしましょう。

例外/警告/アサーションで検出

想定外の値を早めに止めるため、境界チェックにアサーションや例外を使います。

Python
import math

assert -1e12 <= x <= 1e12, "xが想定外の範囲です"
z = f(x)
if not math.isfinite(z):
    raise ValueError("計算結果が有限数ではありません")

開発時に強めのチェック、本番ではログとサンプリングにすると、性能と安全性の両立がしやすいです。

テストで境界値と極端値を確認

境界値テストは最も効果的です。

上限直前、下限直前、ゼロ周り、非常に大きい値や小さい値を入力し、壊れないかを確認します。

浮動小数点ではinfやnanが出ないかも併せて見ます。

目的入力例期待する振る舞い
上限直前の加算INT_MAXと1事前チェックで弾くか例外
小さい数の乗算1e-308と1e-3080.0や極小値になったら警告
大きい数の乗算1e308と1e10inf検出、エラー処理
ほぼ同値の差1.0000001と1.0丸め誤差の説明を理解し許容範囲か判断

まとめ

本記事では、オーバーフローとアンダーフローの基本、原因、検出と回避の実践的ポイントを紹介しました。鍵は次の3点です。1) 型の範囲を理解し、事前に見積もる2) 範囲チェックやisfiniteなどの検査を入れる3) 演算順序やスケーリングで極端な中間値を避ける

さらに、境界値テストで早期に問題を見つけ、必要に応じて安全なライブラリやクランプを組み合わせれば、初学者でも堅牢な数値処理が実現できます。

オーバーフローとアンダーフローを怖がるのではなく、仕組みを知り、手を打つことが大切です。

エンジニアの求人
読み込み中…
プログラミング - データ表現と数値

この記事を書いた人
エーテリア編集部
エーテリア編集部

このサイトでは、プログラミングをこれから学びたい初心者の方に向けて記事を書いています。 基本的な用語や環境構築の手順から、実際に手を動かして学べるサンプルコードまで、わかりやすく整理することを心がけています。

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!