2009-05-05
read time and eval time
anarchy golf - plastic constant やっててはまりかけた話。
long-float-digits
CLISP には long-float-digits という組み込み関数があります。引数なしで、呼び出すと long float の精度 (ビット数?) を返す関数なのですが、それだけではなく setf method を持っています。その (setf long-float-digits) を呼び出すことで、高精度な浮動小数点数が扱えるようになります。
(setf (long-float-digits) 256) (format t "~,50F" (sqrt 2L0)) ;-> 1.41421356237309504880168872420969807856967187537695
ちょっとした落とし穴が
上の例はうまくいくのですが、これを少し変更して、こうしたらどういう結果になるでしょう。ゴルフ以外でこんなことやらないかもしれませんが。
(setf (long-float-digits) 256 a (sqrt 2L0))
setf は (psetf と違って)逐次代入なので、どちらでも結果は同じになるはず。と最初は思ったのですが、実際に a の中身を見てみると
(format t "~,50F" a) ;-> 1.41421356237309504880000000000000000000000000000000
と、精度が上の例より低くなっています。桁数から判断するに、精度は 64 bits (デフォルト) になっているようです。
なんで?
"2L0" という文字列は long float なリテラルとして reader に読み込まれるので、"2L0" がどの精度の値になるかは read time の long-float-precision で決まります。二番目の例では setf form の中に "2L0" が含まれているので、この文字列の解釈は (setf long-float-precision) の呼び出し前に行われることになり、上のような結果になるわけです。
要するに read time と evaluation time の区別を忘れていたのですね。上の結果を見てすぐに「そりゃそうだ」と思われた方も多いかもしれません。
冷静に考えてみれば当たり前にも思えますが、それでもリーダマクロ周りで似たような勘違いをすることはときどきあるので油断できません。