Gauche:浮動小数点数をどこまで読むか

Gauche:浮動小数点数をどこまで読むか

(2008/12/21 15:04:15 PST) きっかけはひとつのバグレポート。小数点以下の桁数が309桁を越える浮動小数点数をGauche (0.8.14) に読ませるとinf.0が返る。

gosh> 1.09861228866810969139524523692252570464749055782274945173469433363749429321860896687361575481373208878797002906595786574236800422593051982105280187076727741060316276918338136717937369884436095990374257031679591152114559191775067134705494016677558022220317025294689756069010652150564286813803631737329857778236
1.0986122886681098
gosh> 1.098612288668109691395245236922525704647490557822749451734694333637494293218608966873615754813732088787970029065957865742368004225930519821052801870767277410603162769183381367179373698844360959903742570316795911521145591917750671347054940166775580222203170252946897560690106521505642868138036317373298577782361
+inf.0

「309桁」でピンと来た人もいるだろうが、0.8.14の数値読み込みルーチンは 小数点以下を一度整数として読み込んで、浮動小数点数に直してスケールしてから ClingerのAlgorithmRを適用している (cf. Gauche:数値の入出力)。 今回の問題は、浮動小数点数に直したところでオーバフローしてしまった ところにある。本来は正確数のまま先にスケールして、得られた有理数を 浮動小数点数に直すべきであった。

ただし、そのようにしたとしても、分母分子が巨大な有理数演算を やるはめになる。もともとdoubleの精度は10進数で17桁程度なのだから、 17桁よりちょっと先まで読むだけで何とかできないだろうか。

17桁より先を単に無視したり丸めてしまうことはできない。 Gauche:Bignum->Doubleで似た問題を扱ったが、 読み込み時にX桁以降を捨てて、さらに浮動小数点数への変換時に2進数53bit目で 丸めると、二重丸めが起きて正しくない数値が得られる場合がある。 この問題は対象桁がどんなに小さくても起こり得るので、小数点数以下の数字は 最後まで読んで使わないとだめなのだ。

ただし、ある桁以降については、それが影響を与える範囲というのは doubleの最後の1bitに限られる。より限定的には、その桁まで読んだ場合の 値がちょうどdoubleで表現できる目盛の真ん中に来る場合で、その時に そっから先がすべて0かそうでないかによって結果が分かれるわけだ。 すべて0かどうかを見るだけなら、全部を読み込んでbignumやらrationalやらで 演算するよりはずっと速い。

では、10進数でどの桁までを読めば良いだろうか。 今仮に、浮動小数点数の精度が(hidden bit含めて)5bitだとしてみる。 2進小数を#bのプレフィクスで表記すると、

#b1.00000 = 1.000
#b1.00001 = 1.03125 
#b1.00010 = 1.0625

#b.1.00001は偶数丸め規則により#b1.0へと丸められる。入力がわずかでも#b1.00001より大きければ、例えば"1.0312500000000000001" であれば、#b1.0001へと丸められることになる。 従ってこの場合、10進数で小数点以下5桁まで正確に読んで、それがちょうど分解能の真ん中に落ちる時に、そこから先がすべて0かどうかを判断すれば良い。

では実際のIEEE doubleではどうなるか。分解能の真ん中に落ちる値は、

#b1.000....0001 (0は52個) = 1.00....0011102230246251565404236316680908203125

で、小数点数以下53桁になる (わざわざ計算しないでも、 1/2してゆくたびに10進数で一桁つづ増えて行くので2^-53なら53桁になるのはわかるけど)

ということは53桁まで読んで、それを正確に浮動小数点数に変換し、 ちょうど中間値に落ちる場合にのみそこから先に0以外があるかを見れば良いのかな。

指数部によるスケーリングの影響がちょっとありそうな気がするな。 後で考える。

Tag: 数値


Last modified : 2008/12/22 00:21:45 UTC