丸め誤差 - おっさんの備忘録

丸め誤差

丸め誤差

小数点以下の数は2進数で表現できないため近似値を使用している。
例えば"0.1(10)"は"0.0001100110011...(2)"となり、"0011"が永遠に循環する。
このため、適当な桁で結果が偶数になるように丸める(最近接偶数への丸め)。

以上から発生する誤差が丸め誤差
COBOLでは丸め誤差は発生しない。

public static double GetByDoubleVariant()
{
  var a = 0.1;
  var b = 0.1;
  var c = 2.5;
  return (a * b) / c; // 0.004000000000000001
}

対処法

整数型で計算

整数型に変換して、計算を行う。 ただし、小数に10n倍しただけでは整数になっていない可能性もあるので、Math.Roundメソッドを使う。

public static double GetByDoubleConvIntVariant()
{
  var a = 0.1;
  var b = 0.1;
  var c = 2.5;

  var aInt = Math.Round(a * 10, MidpointRounding.AwayFromZero);
  var bInt = Math.Round(b * 10, MidpointRounding.AwayFromZero);
  var cInt = Math.Round(c * 10, MidpointRounding.AwayFromZero);
  return ((aInt * bInt) / cInt) / 10; // 0.004
}

任意精度型を使用

C#ではDecimal型が該当する。ただしパフォーマンスが悪い。

public static decimal GetByDecimalVariant()
{
  var a = 0.1m;
  var b = 0.1m;
  var c = 2.5m;

  return (a * b) / c;
}