Java初心者向けです。
Effectivie Javaに「正確な答えが必要ならば、floatとdoubleを避ける」という項目があります。
ここを読んで改めて調べたのでまとめます。
floatとdoubleは誤差があることがある
以下のプログラムを実行してみる。
System.out.println(1.03 - 0.42);
すると、コンソールには以下のように表示される。
0.6100000000000001
期待値は0.61であるが、このように誤差が出てしまう。
なぜ誤差がうまれるのか
float型とdouble型は、科学計算と工学計算のために設計されており、それらは2進浮動小数点算術を行う。
2進浮動小数点算術は、広い範囲の大きさに対して正確な近似を行うために設計されている。そのため正確な結果を提供しない。
(Effectivie Javaより)
2進浮動小数点算術とは
float型とdouble型はIEEE 754の規格で設計されている。
Primitive Data Types
この規格で用いられているのが2進浮動小数点算術である。
浮動小数とは
例として、「1.12」という数字を挙げる。
1.12は以下のようにあらわすこともできる。
$1.12 × 10^0$
$0.112 × 10^1$
$11.2 × 10^{-1}$
このように、(仮数) × (基数)(指数)であらわせる形を浮動小数形式という。
また「1.12」という表現は、浮動小数形式に対して固定小数形式という。
2進浮動小数点は2進数を用いて、小数を表現する方法。
2進浮動小数点算術で誤差が出るのはなぜか
たとえば「0.5」という10進数を2進数で表すとどうなるか。
答えは「0.1」である。
(小数の10進数を2進数に変換するときは、10進数の小数部を小数が0になるまで2倍する)
ほかにも、「0.625(10)」は「0.101(2)」となるし、「0.25(10)」は「0.01(2)」となる。
では「0.1」という10進数を2進数で表すとどうなるだろうか。
答えは「0.00011001100110011......」となり表現しきれない。
よって、2進数の小数では表現しきれないものがあり、誤差になってしまう。
正確な値が必要なときはBigDecimalを使う
たとえばお金の計算を行いたい場合は、正確な値が必要となる。
そんなときはBigDecimalを使用する。
BigDecimal
(詳しい使い方はまた別な記事で)
一番最初に実行してみたプログラムをBigDecimalをつかって実行すると以下のようになる。
BigDecimal num = new BigDecimal("1.03").subtract(new BigDecimal("0.42"));
System.out.println(num.toString());
0.61
ただし、BigDecimalは遅いなどの問題もある。
BigDecimalを使うのか、floatとdoubleを使うのか、を考える必要がある。
(このあたりも別な記事で)
参考
http://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html
http://docs.oracle.com/javase/jp/7/api/java/math/BigDecimal.html