読者です 読者をやめる 読者になる 読者になる

浮動小数点数による計算のこと

computer ruby

コンピュータによって表現された浮動小数点数は近似値であって正確な数ではない。というのは、プログラマのみなさんなら周知の事実だと思いますが、普段あまり意識することがないため、そのことを忘れていたりする人もいるのではないでしょうか。

近似値であるというのは例えば、こういうことです。

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:

とあって、いくつかのリンクが紹介されている。

ここでわかること。

  • 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.ceil2だし、1.1000000000000000001.ceil2です)

つまり、消費税額の計算の例でいうと、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 (PERFECT SERIES 6)

パーフェクトRuby (PERFECT SERIES 6)