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