1

実数直線上の浮動小数点値の分布をよりよく理解しようとしています。

R が10のべき乗である範囲 (-R,R) で一様に分散された表現可能な値の数をカウントするために、このコードを書きました(これも 2 のべき乗で試しました)。

public class Foo {
    public static void main(String[] args)
    {
        for(int i=0; i<24; i++)
        {
            int count = 0;
            float R = (float) Math.pow(10, i); //(2<<i);
            float Rstep = Math.ulp(R);
            for(float x = -R; x <= R; x+=Rstep)
                count++;
            System.out.println(R+" "+count+" "+Math.ulp(R));
        }
    }
}

結果のばらつきに驚きました

1.0 16777217 1.1920929E-7
10.0 20971521 9.536743E-7
100.0 26214401 7.6293945E-6
1000.0 32768001 6.1035156E-5
10000.0 20480001 9.765625E-4
100000.0 25600001 0.0078125
1000000.0 32000001 0.0625

一様に分布する値の数は 16777216 (つまり、23 ビットの仮数部の場合は 1<<23、符号ビットのために 2 倍) になるだろうと半信半疑でした。

この質問の背後に具体性を持たせるために-モデルを構築しようとしています(これは、数桁にわたって正確なSI単位を使用します。たとえば、kmからナノメートルまでの距離です)が、フロート空間にマッピングする必要があります(GPUにロードするため)。これは科学的なモデルなので、どこで精度が失われるかを理解する必要があります。計画は、値を均一に分散された範囲にスナップすることです。したがって、上の表から範囲 (-1000,1000) にスナップすると、32768001 の正確な値が得られます。

これらの範囲にわたって非常に多くの分散があること、および 2 ケースの検出力が制限されている理由は直感に反するように思えます。

これについて考える方法を説明できる人はいますか?

乾杯

4

1 に答える 1

2

浮動小数点数を 16 進数で実際に出力する必要があります。そうすれば、はるかに明確になります。

「16777216 (つまり、23 ビット仮数の場合は 1<<23、符号ビットのために 2 倍)」という見積もりは、期待できる値の半分にすぎません。最良のケースは、16 進数で -0x1.FFF…pX のような数値から開始することです。つまり、2 のべき乗のすぐ下の数値の反対です。最初の ULP を繰り返し追加すると、指数 X を使用して有意桁のすべての値を実際にトラバースします。推測したように、これは 1<<23 ステップです。それが終わると、始めたときの半分のゼロに近づくでしょう。同じステップ数でゼロになり (指数が X よりも小さい)、正の値のステップ数を再び 2 倍にします。

したがって、これは 1<<25 (~32000000) 程度の等間隔のフロートであり、ほぼ 2 のべき乗とその反対の間にあります。1000 は 2 乗の 1024 のすぐ下にあるため、1000 でそのステップ数を得ることができます。

お気づきのように、最悪のケースは、2 の累乗を少し上回る数、たとえば 0x1.00001pX から始めることです。次に、指数 X を持つ値をほとんどトラバースしませんが、代わりに、より低い指数を持つ値をすぐに訪問し始めます。0x1.FFF…pX から開始した場合に比べて、訪問する値の数は半分になります。


注:表記-0x1.123defpXは -0x1.123def * 2 ^ X と解釈する必要があります。おそらく、プログラミング言語は、浮動小数点値の入力および/または出力にそれを受け入れます。繰り返しますが、何が起こっているのかを理解しようとするときに非常に便利です。

于 2013-03-22T22:36:29.467 に答える