C# における「0.1 × 整数」の誤差問題とその対策
1. なぜ誤差が発生するのか
1.1 2進数浮動小数点数と10進数
C# の標準的な浮動小数点型(float
, double
)は、IEEE 754 形式の 2進数ベースで小数を表現します。
一方で「0.1」は 10進数ベースで見ると有限小数ですが、2進数では無限に続く循環小数となり、実際には「格納できる近似値」として扱われます。
0.1 (10進) ≒ 0.0001100110011... (2進, 以下繰り返し)
この「近似値」による 丸め誤差 が生じるため、0.1
や 0.2
など 10進数で有限桁の値でも、コンピュータ内部で完全一致しません。
1.2 乗算や加算で誤差が顕在化
整数 n
に対して n × 0.1
を計算するとき、本来は「n × 0.1
=0.1 × n
」を期待しますが、内部で「正確な0.1」ではなく「2進数で表した近似値」が使われるため、結果にわずかなズレが発生します。
double val = 0.1 * 3;
Console.WriteLine(val); // 0.30000000000000004 など
2. 誤差対策
2.1 decimal
型を使う
C# には 10進数ベースで内部表現する decimal
型が用意されています。小数部分を正確に扱う金融計算や金額管理などでは、float
/ double
ではなく decimal
を使うのが一般的です。
decimal a = 0.1m;
decimal result = a * 3; // 結果は正確に 0.3
- 注意点
double
と比べて取り扱える範囲が狭い- 演算がやや遅い
2.2 最小単位で整数演算する
金額や特定の小数点以下桁数が明確な計算では、
「金額を最小単位(円やセントなど)で整数として扱い、表示時に小数点を付与する」方法も有効です。
// 例: 日本円での金額管理
int total = 100; // 100円
total += 10; // 10円追加、total = 110
Console.WriteLine($"{total}円"); // 出力: "110円"
このアプローチならば浮動小数点誤差は発生しません。
3. まとめ
- 2進数表現では 0.1 を厳密に表せない
→ IEEE 754 浮動小数点を使う以上、丸め誤差は必然。 - 誤差は乗算・加算で顕在化し、期待値との差が生まれる
→ 0.1 を 3 回足しても 0.3 ちょうどにはならない場合がある。 - 対処法として
decimal
型や整数演算を利用する
→ 金融計算や正確性が要求される場合は 10進数で保持する仕組みを使う。
「0.1」をはじめとする多くの 10進数値は、2進数の有限ビットで厳密に表現できません。よって C#(に限らずほとんどのプログラミング言語)では、「わずかな誤差」が生じることを前提に設計や実装を行う必要がある ことを念頭に置いてください。
ディスカッション
コメント一覧
まだ、コメントがありません