あいさつと経緯
初めまして!
0.8+0.9って1.7でしょ?何言ってんの?っていう方もいるかもしれません。
このタイトルの背景は、私がこの業界に入って初めてJavaに触れた時、注意された言葉から来ています。
「計算処理ではBigDecimalを使用しなさい」
言われた方もいるのではないでしょうか?計算処理で誤差が出ないようにするためで理解している方は多いと思います。
この記事では、タイトルとBigDecimalの何が紐づくのかお話ししていこうと思います。
なぜ0.8+0.9は1.7000000000000002なのか?
早速タイトル回収です。
タグにも入れましたが、基本情報技術者試験の勉強された/されている読者の方もいるでしょうか?
2進数が今回のポイントになります。
0.8+0.9は人間にとっては単純です。
ですが、パソコンはタイトルのように間違えます。
何が違うのか?
それは、計算方法です。
人間は10進数で0.8+0.9を計算しています。(違う進数で計算されている方がいたらすいません)
パソコンは2進数で0.8+0.9を計算しようとするのです。
2進数で0.8+0.9を計算するとどうなるのか?
0.8+0.9の前に、整数の8+9を計算してみましょう。
ここからは下記のサイトを使用すると理解しやすいです。
整数の8+9を二進数で計算すると・・・?
8を二進数に変換すると・・・
1000
9を二進数に変換すると・・・
1001
両者を足してみましょう
1000+1001=10001
10001を10進数に直すと17になります。
小数の0.8+0.9を二進数で計算すると・・・?
さて本題です。そもそも小数の場合どう表現するでしょうか?
ちなみにこれは基本情報技術者試験に出てきますね。
そうです。位が下がるたびに値を半分にして表現します。
0.000
1→1/2→1/4→1/8....
この法則を見てなんとなく想像もつく方がいると思いますが、実際に計算を見てみましょう
0.8を二進数に変換すると・・・
0.11001100110011001100110011001100110011001100110011...
0.9を二進数に変換すると・・・
0.11100110011001100110011001100110011001100110011001...
見ての通り、半分の値で桁数を変える小数では値を表現しきれないのです。
その結果何が起こるかというと、丸め誤差が発生します。ここも基本情報技術者試験に出ますね。
結果として以下の数値が出てきます。
1.10110011001100110011001100110011001100110011001101...
これを10進数に変換すると
1.7000000000000002
1.7000000000000002に丸められることとなります。
値が少し増えてしまうのも、0.8と0.9で丸められ
本来の値より大きい値で計算されたためです。
計算処理ではBigDecimalを使用しなさい
小数の計算は、桁数が大きくなると丸められてしまい正しい値を求められないことがわかりました。
BigDecimalは、とても単純に言ってしまうと小数計算はしておらず、演算部分で整数に直してから計算をしています。
[単純な例]
0.8を8にする。
0.9を9にする。
8+9=17
上げた桁数分を戻す。
1.7
結論:0.8+0.9は1.7である
2進数で小数計算を行うと丸め誤差で思わぬ計算間違いが起こります。
それぞれの言語がそもそも対応しているのか。メソッドを使わないといけないのか?
十分に気をつけて対応しましょう。
基本情報技術者試験勉強している方、応援しています!