浮動小数点数による計算のこと
コンピュータによって表現された浮動小数点数は近似値であって正確な数ではない。というのは、プログラマのみなさんなら周知の事実だと思いますが、普段あまり意識することがないため、そのことを忘れていたりする人もいるのではないでしょうか。
近似値であるというのは例えば、こういうことです。
irb(main):081:0> 2.2 - 2.1 == 0.1 => false
2.2 - 2.1
の答えは0.1
ですが false になっていますね。
2.2 - 2.1
の結果を表示するとその理由がわかります。
irb(main):082:0> 2.2 - 2.1 => 0.10000000000000009
で、浮動小数点数について今回いろいろと調べたので、その記録としてこれを書いています。
浮動小数点数って?
ここで説明するには難しすぎるし、自分自身、理解しきれていないので、有用そうなURLを書いておきます。
Float objects represent inexact real numbers using the native architecture's double-precision floating point representation.
Floating point has a different arithmetic and is an inexact number. So you should know its esoteric system. see following:
とあって、いくつかのリンクが紹介されている。
- What Every Computer Scientist Should Know About Floating-Point Arithmetic
- リンク切れぽい
- Ruby Talk FAQ · rdp/ruby_tutorials_core Wiki · GitHub
- Floating point - Wikipedia, the free encyclopedia
ここでわかること。
- Float は不正確な実数を表現している
- 計算方法が変わっていて難しいので、リンク先を見てね
- native architecture’s ってのがわからなかったけど、ソフトウェアじゃなくてハードウェアを使って演算してるという感じ?なのかな・・?
あとは、以下の記事がとてもわかりやすかったです。
消費税額の計算
浮動小数点数の誤差が問題になるケースの1つは、計算結果の端数を切り上げる場合です。 例えば消費税額の計算などで、以下の記事に詳しいです。
上記の記事では Class: BigDecimal (Ruby 2.1.5) を使って解決しています。ざっくりいうと、BigDecimal を使って10進数で計算することで2進数では小数を完全に表現できないという制限を回避しています。
もうひとつ解決策があって、上記の記事のコメント欄にも書かれていますが、整数で計算してから割り算するという方法です。そうすることで、記事中の問題である1.08
の浮動小数点数の誤差がなくなるので、正確に計算できるようになります。
で、「整数で計算してから割り算するという方法」というのは本当に問題ないのでしょうか?
数を切り上げるときの問題点
1.0
を小数点第一位で切り上げても1.0
のままですが、1.000000001
と切り上げると2
になってしまいます。
irb(main):094:0> 1.0.ceil => 1 irb(main):095:0> 1.000000001.ceil => 2
これが問題で、それ以外のケースでは切り上げによる問題は起きないでしょう。
(たとえば1.1.ceil
は2
だし、1.1000000000000000001.ceil
も2
です)
つまり、消費税額の計算の例でいうと、100
で割った結果、小数点第一位以下が0
のケースで誤差があると問題なのだと思いました。
こういうケースを考えます。
irb(main):109:0> 50 * 108 => 5400 irb(main):110:0> 50 * 108 / 100.0 => 54.0
この例では問題なさそうです。
では、5400 / 100.0
のような計算をしたときに誤差は発生しうるのでしょうか?
小数点以下が必ず.00
になるケースです。
私は発生しない、と思いました。
なぜなら、2進数で計算したときに必ず割り切れるからです。 割り切れるのだから誤差は発生しません。
一方で・・
(上の流れを引き継ぎます)
一方で、50 という数字が計算結果によって作られた数字だったらどうでしょう? 例えば(もはや何の例えか意味がわかりませんが)0.1 を 500 回足した結果の 50 だったら?
irb(main):111:0> sum = 0.0 => 0.0 irb(main):112:0> 500.times do irb(main):113:1* sum += 0.1 irb(main):114:1> end => 500 irb(main):115:0> sum => 50.00000000000044
この場合、すでに誤差が発生してしまっているので問題が起きてしまいそうです。
(むりやり)まとめ
浮動小数点数の誤差というのはこのように、必ずしも問題が起きるわけではなくて、使いどころによっては問題ないケースもあります。プログラマたるべきもの、浮動小数点の仕組みを(そこそこ)把握して、ここは浮動小数点で大丈夫か?ということを自問しながらプログラムを書いていきたいものです。
(ちなみに、ここは浮動小数点で大丈夫か?を考えるというのは人の受け売りです・・・)
- 作者: Rubyサポーターズ,すがわらまさのり,寺田玄太郎,三村益隆,近藤宇智朗,橋立友宏,関口亮一
- 出版社/メーカー: 技術評論社
- 発売日: 2013/08/10
- メディア: 大型本
- この商品を含むブログ (16件) を見る