浮動小数の誤差

2進計算による浮動小数の誤差について意識なく平気で演算したり比較するケースをよく見かけます。
大抵はあまり問題は発生しないのですが、特殊なケースで予想と反する結果となります。

色々と試してみました。

irb(main):001:0> 1-0.9 == 1/10
=> false

RubyでFixnum型での浮動小数は2進数計算となるようです。
小数表現が内部的に2^nであらわされているために発生する問題です。

10進で値を保持するbigdecimalを使用するのが妥当な解決策と思われます。

irb(main):001:0> require 'bigdecimal'
irb(main):002:0> n1 = BigDecimal("1")
irb(main):003:0> n09 = BigDecimal("0.9")
irb(main):004:0> n10 = BigDecimal("10")
irb(main):005:0> n1-n09 == n1/n10
=> true
System.out.println(1-0.9==1/10);

falseが出力されます。
回避策は同じく10進数保持のjava.math.BigDecimalを使用します。

BigDecimal bd1 = new BigDecimal("1");
BigDecimal bd09 = 
  new BigDecimal("0.9")
  .setScale(2,BigDecimal.ROUND_HALF_UP);//有効桁数2で四捨五入
BigDecimal bd10 = new BigDecimal("10");
System.out.println(
  bd1.subtract(bd09).equals(bd1.divide(bd10,2,BigDecimal.ROUND_HALF_UP))
);

trueが出力されます。

alert(1-0.9==1/10);

falseが出力されます。
やはりNumber型も2進計算のようです。
javascriptでは標準では10進数保持の型は存在しないようで、
原始的に有効桁数を掛け算した後に演算を行い、有効桁数で割り算を行うのが一般的なようです。


javascript用のbigdecimalパッケージも存在はしているみたいですが、わざわざ使用するのも微妙な気がしますね。


問題は除算の場合に意識がある人は多いのですが、小数の四則演算が危険という認知は若干低いように思います。
小数誤差は昔からの一般常識でありコーダの意識の問題だと耳にすることもありますが、最近の高級言語では使用頻度からDecimal型が通常の数値型という訳にはいかないものなのかなと考えたりします。