1

次の関数を使用して浮動小数点数が切り捨てられる、決定論的シミュレーションを構築しようとしています:(ここで見つけました:http: //joshblog.net/2007/01/30/flash-floating-point-number-エラー

return Math.round(10000 * float) / 10000;

私の質問は、私がそれを10000で割っているという事実自体が、フロートポイントエラーの原因ではないかということです。 IEは、除算が行われるたびに、新しいフロートになり、さらに非決定的な結果が生じる可能性があります。

編集:これはどうですか?2の累乗のみを使用

return Math.round(float* 1024) / 1024;
4

4 に答える 4

2

決定論的と言うときは、シミュレーションを実行するたびにまったく同じ結果が得られる再現可能なシミュレーションが必要だと思います。

これを実現するには、考えられる変動の原因を見つけて排除する必要があります。

唯一の方法は、特定のアーキテクチャのバイナリにコンパイルすることです。

浮動小数点演算自体は完全に指定されています。浮動小数点標準(IEEE-754)は、すべての最新のプロセッサに準拠しており、あいまいさはありません。

2つの主なバリエーションがあります。

  1. 命令セットの違い。これは最も明白なものです。アプリケーションを32ビットまたは64ビットにコンパイルすると、わずかに異なる結果が得られる可能性があります。32ビットアプリケーションは、80ビットの中間値を使用する古いスタイルのx87命令を使用する傾向があります。これにより、一部の結果の丸めが異なります。x86でも、一度に複数のオペランドを処理するSSE命令を使用すると、違いがあります。一部のコンパイラは、オペランドがメモリ内でどのように整列されるかに依存するコードを生成する場合があります。

  2. 命令の順序の違い。数学的(a+b)+ca+(b+c)は、同等です(加算は結合法則です)。浮動小数点計算では、これは当てはまりません。aが1、bマイナス1、およびに丸められるcような小さな数値の場合、式はそれぞれとに評価されます。使用する命令を決定するのはコンパイラです。言語とプラットフォームに応じて、言語コンパイラまたはジャストインタイムIL/バイトコードコンパイラの場合があります。いずれにせよ、コンパイラはブラックボックスであり、私たちの知らないうちにコードをコンパイルする方法を変える可能性があります。最小の違いは、異なる最終結果につながる可能性があります。1+c1c0

丸めのアプローチは理論的には見栄えがしますが、機能しません。どのように丸めても、2つの異なるが同等の命令のセットが、異なる方法で丸められる結果を生成する場合が常にあります。

主な理由は、数字に丸めてから数字に丸めることは最初から数字に丸めることと同等ではないという意味で、丸めは構成できないというaことb (< a)ですb。例:1.49を1桁に丸めると1.5になり、それを0桁に丸めると2になります。ただし、0桁に丸めると直接1になります。

したがって、中間値に80ビットの「拡張」精度を使用するx87ベースのシステムでは、64の有効ビットから開始します。これを希望の精度に直接切り捨てることができます。倍精度の中間体がある場合、同じ中間結果が得られますが、53の有効ビットに丸められ、次に希望の精度に丸められます。

唯一のオプションは、特定のアーキテクチャ用のマシンコードを作成することです。

違いを完全になくすことではなく、違いを最小限に抑えることが目標である場合、答えは簡単です。2の累乗(1024など)で除算または乗算しても、使用する範囲に追加の丸め誤差は発生しません。アプリケーション、1000のように乗算と除算を行います。

エラーの累積をランダムウォークと見なす場合、丸めに1000を使用すると、1024を使用するよりも多くのステップが必要になります。乗算と除算の両方で追加のエラーが発生する可能性があります。したがって、平均すると、合計エラーが大きくなるため、丸め操作が間違った方向に進む可能性が高くなります。これは、すべての操作を丸めるときにも当てはまります。

于 2012-07-06T21:43:59.023 に答える
2

私の目標は、プラットフォーム(C#/ AS3および32/64ビット)間でより高い一貫性を実現することでしたが、100%の一貫性は不可能であることを認めています。(AS3はすべての内部操作が浮動小数点数を介して実行されるため、実数演算ができないため)

私がこれまでに集めたもの(EricPostpischilとJeffreySaxに感謝します):

Math.round(1024 * float) / 1024;

上記のうち、「Math.round(1024 * float) 」操作は、「単一の操作内でも可能である」「エラーがクォンタムの半分以上に蓄積された」場合、すべてのプラットフォームで同じ結果を生成しない可能性があります。 。

  • これは数学的には可能ですが、非常にまれである可能性が高いため、全体として、この操作は生成されるよりも多くの不整合を排除します。したがって、プラットフォーム間の不整合を減らすため、実行する価値があります(ただし、それらを排除することはできません)。

「/ 1024 の部分については、1024は2の累乗、つまりストレートビットシフトであるため、余分なエラーは発生しません。1000で割ると、余分なエラーが発生する可能性がわずかになります。 1000を完全に表すことはできません。したがって、1000で除算すると、1024で除算できなかった丸め後に別のエラーが発生する可能性があります。

結論: Math.round(1024 * float)/ 1024; Math.round(1000 * float)/1000よりも優れています。どちらも完璧ではありませんが。

これは正確な記述ですか?

于 2012-07-08T14:05:32.763 に答える
0

10,000で除算すると、正確な数学的結果と倍精度で表現可能な最も近い数値との差に等しい丸め誤差が発生します。これは、IEEE754バイナリ浮動小数点演算が最も近い丸めモードであると仮定した場合です。このエラーは、結果の最大で1/2 ULP(最小精度の単位)です。

2の累乗で乗算し、整数に丸め、同じ2の累乗で除算しても、次の場合を除いて、丸め操作でエラーは発生しません。正確な結果が約2 1024(正確なしきい値はわずかに遅い)以上の乗算浮動小数点の無限大を生成します。(一般に、2の累乗による乗算または除算は、結果が浮動小数点範囲をアンダーフローする場合、つまり正確な数学結果が(0、2 -1022 )の場合、丸め誤差を生成する可能性があります。ただし、アンダーフローは次の場合には発生しません。 pのround(x * p)/ pを計算すると、2の正の累乗が2 1023未満になります。)

この方法で数値を量子化しても、一般に決定論的な結果は得られません。事前量子化値にエラーがある場合、2つのプラットフォーム間の偏差が発生する可能性があり、量子間の中間点にまたがる可能性があります。

于 2012-07-08T11:01:47.233 に答える
0

これは、量子の倍数に丸めても、スケーリングにエラーがない場合でも決定論的な結果が得られないことを示すコードです。

私が得る出力は次のとおりです。

Machine 0 produces 0x1p+0 (1).
Machine 1 produces 0x1.004p+0 (1.0009765625).
The results differ.

ソースコードは次のとおりです。

#include <stdio.h>
#include <math.h>


// Round a value to the nearest multiple of the quantum.
static double Quantize(double x)
{
    static const double Quantum = 1024., InverseQuantum = 1/Quantum;

    return round(x * Quantum) * InverseQuantum;
}


int main(void)
{
    /*  For this example, we are in the middle of some calculation, where we
        have some value a from earlier operations.  a0 and a1 represent the
        calculated values of a on two different platforms.  Observe that the
        difference is as small as possible, just a single ULP.
    */
    double a0 = 0x1.cbd9f42000000p0;
    double a1 = 0x1.cbd9f42000001p0;

    // Define a constant that the calculation uses.
    double b = 0x1.1d2b9fp-1;

    // Calculate the pre-quantization result on each machine.
    double x0 = a0 * b;
    double x1 = a1 * b;

    // Quantize the result on each machine.
    double y0 = Quantize(x0);
    double y1 = Quantize(x1);

    // Display the results.
    printf("Machine 0 produces %a (%.53g).\n", y0, y0);
    printf("Machine 1 produces %a (%.53g).\n", y1, y1);
    printf("The results %s.\n", y0 == y1 ? "are identical" : "differ");

    return 0;
}
于 2012-07-08T13:53:26.297 に答える