LoginSignup
16
13

More than 5 years have passed since last update.

【Java初心者】いざとなったらよくわからなかったので改めて調べた~floatとdoubleに誤差が出る理由~

Last updated at Posted at 2015-05-14

Java初心者向けです。

Effectivie Javaに「正確な答えが必要ならば、floatとdoubleを避ける」という項目があります。
ここを読んで改めて調べたのでまとめます。

floatとdoubleは誤差があることがある

以下のプログラムを実行してみる。

java
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をつかって実行すると以下のようになる。

java
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

16
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
13