19

C/C++ の場合と同様に、 のような最小の表現可能な値であるという定義を使用して、Java でdouble マシンのイプシロンを決定しようとしています。ウィキペディアによると、このマシンのイプシロンは(52 は仮数ビット数 - 1)と等しくなります。doublex1.0 + x != 1.02^-52double

私の実装では次のMath.ulp()関数を使用します。

double eps = Math.ulp(1.0);
System.out.println("eps = " + eps);
System.out.println("eps == 2^-52? " + (eps == Math.pow(2, -52)));

結果は私が期待したものです:

eps = 2.220446049250313E-16
eps == 2^-52? true

ここまでは順調ですね。ただし、与えられたepsが実際にそのような最小 であることを確認すると、より小さいもの、別名によるの値があるようです:x1.0 + x != 1.0 doubleMath.nextAfter()

double epsPred = Math.nextAfter(eps, Double.NEGATIVE_INFINITY);
System.out.println("epsPred = " + epsPred);
System.out.println("epsPred < eps? " + (epsPred < eps));
System.out.println("1.0 + epsPred == 1.0? " + (1.0 + epsPred == 1.0));

どちらが得られますか:

epsPred = 2.2204460492503128E-16
epsPred < eps? true
1.0 + epsPred == 1.0? false

ご覧のとおり、定義に反して、1 に追加すると 1 にならないマシン イプシロンよりも小さいものがあります。

では、この定義による一般的に受け入れられているマシン イプシロンの値のどこが間違っているのでしょうか? それとも私は何かを逃しましたか?浮動小数点演算の別の難解な側面を疑っていますが、どこが間違っていたのかわかりません...

編集:コメント者のおかげで、私はついにそれを手に入れました。私は実際に間違った定義を使用しました! eps = Math.ulp(1.0)は、表現可能な最小の double > までの距離を計算しますがそれ1.0がポイントです。それはで最小でepsなく、その値の約2 倍です。x1.0 + x != 1.01.0 + Math.nextAfter(eps/2)1.0 + eps

4

2 に答える 2

21

C/C++ と同様に、1.0 + x != 1.0 となる表現可能な最小の double 値 x であるという定義を使用します。

これは、Java でも C でも C++ でも定義されたことはありません。

定義は、マシン イプシロンは、1 と 1 より大きい最小の float/double の間の距離であるということです。

あなたの「定義」は2 倍近く間違っています。

また、 が存在しない場合はstrictfp、より大きな指数範囲が許可されるだけであり、イプシロンの経験的測定に影響を与えるべきではありません。これは、イプシロンが1.0とその後継から計算され、それぞれとその差が標準の指数範囲で表すことができるためです。

于 2015-02-26T13:21:35.113 に答える
7

あなたの実験方法/理論が正しいかどうかはわかりません. Math クラスのドキュメントには次のように記載されています。

特定の浮動小数点形式の場合、特定の実数値の ulp は、その数値を囲む 2 つの浮動小数点値の間の距離です。

ulpメソッドのドキュメントには次のように書かれています。

double 値の ulp は、この浮動小数点値と次に大きさが大きい double 値との間の正の距離です。

したがって、 のような最小eps値が必要な場合は、少なくとも より大きい値の場合、結果が切り上げられるため1.0 + eps != 1.0、eps は実際にはよりも小さい必要があります。 Math.ulp(1.0)Math.ulp(1.0) / 2

そのような値の最小値は で与えられると思いますMath.nextAfter(eps/2, 1.0)

于 2015-02-26T13:21:31.437 に答える