Java: Re: 浮動小数点数

[id:lethevert:20070612:p2]の件だけれど、printf()がどうなっているのか調べてみる。
とりあえず、手元のJDK1.6に付いてきているsrc.zipを確認する。
PrintStream#printf()を見てみると、

formatter = new Formatter((Appendable) this);

とかやっている。Formatterと言うのはjava.util.Formatterのことなので、そこを調べるに、Double.toString()に従っているとのこと。
確かに、Double.toString()も切りのいい数字で表示するのでなるほどというところ。
で、Double.toString()を確認すると

return new FloatingDecimal(d).toJavaFormatString();

となっている。このFloatingDecimalというのはsun.misc.FloatingDecimalなのだけれど、これはsrc.zipに含まれていないし、Googleってもドキュメントが見つからない。
そこで、OpenJDKのソースリポジトリにあたってみた。sun.misc.FloatingDecimal

      • -

FloatingDecimal#dtoa()という非常に長いメソッドがあって、これが2進小数を10進小数に変換しているみたい。
このなかでは、easy caseとそうでないのを分けて、eazyでない場合は次のコメントにあるような処理を行っている。

	/*
	 * This is the hard case. We are going to compute large positive
	 * integers B and S and integer decExp, s.t.
	 *	d = ( B / S ) * 10^decExp
	 *	1 <= B / S < 10
	 * Obvious choices are:
	 *	decExp = floor( log10(d) )
	 * 	B      = d * 2^nTinyBits * 10^max( 0, -decExp )
	 *	S      = 10^max( 0, decExp) * 2^nTinyBits
	 * (noting that nTinyBits has already been forced to non-negative)
	 * I am also going to compute a large positive integer
	 *	M      = (1/2^nSignificantBits) * 2^nTinyBits * 10^max( 0, -decExp )
	 * i.e. M is (1/2) of the ULP of d, scaled like B.
	 * When we iterate through dividing B/S and picking off the
	 * quotient bits, we will know when to stop when the remainder
	 * is <= M.
	 *
	 * We keep track of powers of 2 and powers of 5.
	 */

で、この'M'というのがキーで、これは'(1/2) of the ULP of d'ということで、ULPとはおそらくjava.lang.StrictMath#ulp(double)のことと同じだと思う。
つまり、浮動小数点数で表される数を中心とした幅ULP分の近傍に存在する数のうち、10進数で表してもっとも少ない桁で表される数を求めているということかな?
というわけで、どうもJavaの中の人は、浮動小数点数を正確な数ではなく、近似的な数として考えているのかな、と思いました。

      • -

ちなみに、次のコメントにちょっと笑った。

	/*
	 * Construct, Scale, iterate.
	 * Some day, we'll write a stopping test that takes
	 * account of the asymmetry of the spacing of floating-point
	 * numbers below perfect powers of 2
	 * 26 Sept 96 is not that day.
	 * So we use a symmetric test.
	 */

もう2007年ですが。