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.10.2 など 10進数で有限桁の値でも、コンピュータ内部で完全一致しません。

1.2 乗算や加算で誤差が顕在化

整数 n に対して n × 0.1 を計算するとき、本来は「n × 0.10.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. まとめ

  1. 2進数表現では 0.1 を厳密に表せない
    → IEEE 754 浮動小数点を使う以上、丸め誤差は必然。
  2. 誤差は乗算・加算で顕在化し、期待値との差が生まれる
    → 0.1 を 3 回足しても 0.3 ちょうどにはならない場合がある。
  3. 対処法として decimal 型や整数演算を利用する
    → 金融計算や正確性が要求される場合は 10進数で保持する仕組みを使う。

「0.1」をはじめとする多くの 10進数値は、2進数の有限ビットで厳密に表現できません。よって C#(に限らずほとんどのプログラミング言語)では、「わずかな誤差」が生じることを前提に設計や実装を行う必要がある ことを念頭に置いてください。

C#

Posted by hidepon