Island Life

< 創作の目的 | 偽ポインタ >

2010/08/20

浮動小数点数遊び

ついったーで

Gaucheで(apply + (iota 10 0.1 0))を評価すると、0.999999999..と結果が出るのだが、他の言語(Rubyとか)だと1.0と出る。なんでだ?丸め誤差を出してる精度の違い?・・・なの

という話を見かけて、これの答えは以下の通りなんだけど:

http://practical-scheme.net/chaton/gauche/a/2010/08/20#entry-4c6e3eb1-56a43

計算ではなく表示精度の問題だと思います RT: @illness072: Gaucheで(apply + (iota 10 0.1 0))を評価すると、0.999999999..と結果が出るのだが、他の言語(Rubyとか)だと1.0と出る。なんでだ?丸め誤差を出してる精度の違い?・・・なの

irb(main):001:0> 0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1
=> 1.0
irb(main):002:0> (0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1+0.1) == 1.0
=> false

@illness072 Gaucheの浮動小数点数表示は内部表現と1:1に対応しているので、同じ表記で出力された数値は正確に一致します。つまり、正確に一致しない数値を同じ表記で出力することはありません。
ああ、正確にはNaNだけ例外か。複数のビットパターンがあるけど全部+nan.0と表示するから。

ふらふらと他のツイートをみてたらこんな問題を見つけた。

http://twitter.com/s01/status/21375206236

「1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0の10個と + - × ÷ ( )を使って大きい数を作れ」という問題。10万以上から「ちょっと賢い」ラインだそうだけど1万も厳しい

本来は数学の問題なんだと思うけど、 プログラマならやっぱり誤差を利用することを考えちゃうよね。

gosh> (define *s* '(1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0))
*s*
gosh> (for-each (^(a b) (print "(- "a" "b")="(- a b))) (cdr *s*) *s*)
(- 1.2 1.1)=0.09999999999999987
(- 1.3 1.2)=0.10000000000000009
(- 1.4 1.3)=0.09999999999999987
(- 1.5 1.4)=0.10000000000000009
(- 1.6 1.5)=0.10000000000000009
(- 1.7 1.6)=0.09999999999999987
(- 1.8 1.7)=0.10000000000000009
(- 1.9 1.8)=0.09999999999999987
(- 2.0 1.9)=0.10000000000000009

ご覧の通り、10個の数値は等間隔ではなく、0.1より微妙に狭い場合と 微妙に広い場合の2通りの間隔がある。微妙に広いのと微妙に狭いのの差を 取ればうんと小さい数が作れるので、例えば:

gosh> (/ (* 1.6 1.5)
         (* (- (- 2.0 1.9) (- 1.2 1.1))
            (- (- 1.8 1.7) (- 1.4 1.3))))
4.867778304876402e31

(ところで数学的にやるとどこまでいけるんだろう。 今のところ10万ちょっと越えくらいまでしか思いつかない。 誤差を避けるために有理数で計算:

gosh> (/ (* 12/10 18/10)
         (- (- (/ 13/10 11/10) (/ 17/10 16/10))
            (- (/ 20/10 19/10) (/ 14/10 15/10))))
541728/5
gosh> (inexact 541728/5)
108345.6

)

(ああ、分子を足し算にすれば150480になるな。)

Tag: Programming

Post a comment

Name: