Gauche:Bignum->Double

Gauche:Bignum->Double

Shiro: bignumをflonumに変換する際に、2重丸めが行われて誤差が発生する問題。

PHP以外全員不正解のテストを 試していて気づいた。

正確数

<----53bit---->
100000....00000100...001

は、IEEE倍精度浮動小数点数に変換した際に

<----53bit---->
100000....00001000...000

となるべきなのに、

<----53bit---->
100000....00000000...000

となってしまう、という問題。 

原因はScm_BignumToDouble()にあった。 Bignumの各ワード (上位3ワードまで) を浮動小数点数演算で足し合わせているんだけれど、 下位ビットが(丸めを考慮される以前に)有効数字外として切り捨てられると 次のように端数が分解能の中央に来てしまうために、偶数丸めによって0方向に 丸められてしまう。

<----53bit---->
100000....00000100...000

まあ、ある意味浮動小数点数の限界なわけで、bignumでなくてもこのように 誤差は発生するわけだが:

gosh> (+ (expt 2.0 65) (expt 2.0 12) 1)
3.6893488147419103e19
;; 本当は3.689348814741911e19の方が近い

「演算」ではない単なる変換操作でこういう誤差が入るのは悔しい。

解決策

問題が出るのはbignumの上位54ビットがこういうパターンの時(で、かつxxx..xxに 最低ひとつは'1'がある時):

<---53bits---->
1.............01xxxxx..xxx

なので、こういうパターンの時だけ特別扱いする。

いやだめだ。演算誤差の蓄積という意味では、次のようなパターンで、 yyy...yyyの部分で途中に切り上げが発生してしまった場合にやっぱり判断を誤る。

<---53bits---->
1.............00111...111yyy...yyy

というわけで、浮動小数点演算に頼らず、自分でbignumから53bitを切りだして 丸め処理も自前で行うのが良さそうだ。

関連

Gauche:数値の入出力, Gauche:拡張浮動小数点演算の謎

Tag: 数値

More ...