実数の表示方法によって精度が足りないように見えることがある

プログラマのための数学#2を眺めていました。途中から(あまりにもメジャーな処理系である)SML#とNJをいじり始めて聞いてませんでしたけど…。
で、いきなりバグ見つけて混乱したり。

で、本題です。SMLの実数型であるReal.realがIEEE754準拠であることが要求されますが*1精度は指定されてません。SML/NJ,SML#,MLtonにはどれも倍精度で提供されています。(RealもOptionalで入ってる)
上の講義資料のpythonと同じようにreplに打ち込むと桁数が足りないように見えます。

# 1.0+1e~12;
val it = 1.000000000001 : real
# 1.0+1e~13;
val it = 1.0 : real

これはreplでReal.toString(fmt (StringCvt.GEN NONE))が呼ばれて12桁以降は捨てられてるからでしょう。
フォーマットを明示すればより下の桁までちゃんと表示出来ます。が、またも微妙に怪しい結果を出してしまった。

実験

以下のコードを使って処理系毎の扱いを確認しました。

infixr 1 $;
fun f $ a = f a;
fun println s = print (s^"\n")

val _ =
(
    println "toString";
	println (Real.toString (1.0+1e~10));
	println (Real.toString (1.0+1e~11));
	println (Real.toString (1.0+1e~12));
	println (Real.toString (1.0+1e~13));
	println (Real.toString (1.0+1e~14));
	println (Real.toString (1.0+1e~15));
	println (Real.toString (1.0+1e~16));
    println "FIX(SOME 20)";
	println (Real.fmt (StringCvt.FIX(SOME 20)) (1.0+1e~14));
	println (Real.fmt (StringCvt.FIX(SOME 20)) (1.0+1e~15));
	println (Real.fmt (StringCvt.FIX(SOME 20)) (1.0+1e~16));
    println "EXACT";
	println (Real.fmt (StringCvt.EXACT) (1.0+1e~14));
	println (Real.fmt (StringCvt.EXACT) (1.0+1e~15));
	println (Real.fmt (StringCvt.EXACT) (1.0+1e~16))
)

まずSML#から。

(* SML# *)
toString
1.0000000001
1.00000000001
1.000000000001
1.0
1.0
1.0
1.0
FIX(SOME 20)
1.00000000000000999201
1.00000000000000111022
1.00000000000000000000
EXACT
0.100000000000001E1
0.1000000000000001E1
0.1E1

次にSML/NJでは…

(* SML/NJ *)
toString
1.0000000001
1.00000000001
1.0
1.0
1.0
1.0
1.0
FIX(SOME 20)
1.00000000000001000000
1.00000000000000000000
1.00000000000000000000
EXACT

uncaught exception Fail [Fail: RealFormat: fmtReal: EXACT not supported]
  raised at: Basis/Implementation/real-format.sml:269.8-269.55

StringCvt.EXACT実装してないのか…。

さらにMLtonでは…

toString
1.0000000001
1.00000000001
1
1
1
1
1
FIX(SOME 20)
1.00000000000000999201
1.00000000000000111022
1.00000000000000000000
EXACT
0.100000000000001E1
0.1000000000000001E1
0.1E1

となります。

考察

…んー? SML/NJは精度が1桁足りない気がしますね?
どっちもReal.precision=53だから精度は同じなはずなんだけど。仮数部は足りてるけど指数部は足りなくてもいいのか?怪しいです。
MLtonもReal.toStringの時だけ桁が足りてないけど、EXACTを見れば内部的には足りてそうなので、これは桁の数え方が違うんだと思います。


あとは Real.toString 1.0 の結果がMLtonとその他で異なっています。

The string should display as many significant digits as possible, subject to this maximum. There should not be any trailing zeros after the decimal point. There should not be a decimal point unless a fractional part is included.

http://www.standardml.org/Basis/string-cvt.html#SIG:STRING_CVT.realfmt:TY:SPEC

MLtonが正しそうかな?


全部微妙に怪しいじゃないか。困った。