質問

これはおそらくx86 FPUの専門家に対する質問です。

範囲[min、max]でランダムな浮動小数点値を生成する関数を記述しようとしています。問題は、ジェネレーターアルゴリズム(浮動小数点Mersenne Twister、好奇心が強い場合)が範囲[1,2)の値のみを返すことです。つまり、包括的な上限が必要ですが、「ソース」は生成される値は、排他的な上限からのものです。ここでのキャッチは、基になるジェネレーターが8バイトのdoubleを返すことですが、4バイトのfloatのみが必要であり、デフォルトのFPU丸めモードのNearestを使用しています。

私が知りたいのは、この場合の切り捨て自体が、FPUの内部80ビット値が十分に近いときに最大値を含む戻り値になるか、または前に最大値の仮数をインクリメントする必要があるかどうかです[1,2)で中間ランダムを乗算するか、FPUモードを変更する必要があるかどうか。もちろん、他のアイデアも。

現在使用しているコードは次のとおりです。1.0fが0x3f800000に解決されることを確認しました:

float MersenneFloat( float min, float max )
{
    //genrand returns a double in [1,2)
    const float random = (float)genrand_close1_open2(); 
    //return in desired range
    return min + ( random - 1.0f ) * (max - min);
}

違いがある場合、これはWin32 MSVC ++とLinux gccの両方で動作する必要があります。また、SSE最適化のバージョンを使用すると、これに対する答えが変わりますか?

編集:答えはイエスです。この場合、doubleからfloatへの切り捨ては、結果に最大値を含めるのに十分です。詳細については、Crashworksの回答を参照してください。

役に立ちましたか?

解決

SSE opsは、中間の80ビット表現を持たないため、このアルゴリズムの動作を微妙に変更します。計算は、32ビットまたは64ビットで行われます。良いニュースは、/ ARCH:SSE2コマンドラインオプションをMSVCに指定するだけで簡単にテストし、結果が変わるかどうかを確認できることです。これにより、通常の浮動小数点に対してx87 FPU命令の代わりにSSEスカラーopsが使用されます数学。

正確な丸め動作が整数境界の周りにあるかどうかはわかりませんが、1.999 ..が eg

static uint64 OnePointNineRepeating = 0x3FF FFFFF FFFF FFFF // exponent 0 (biased to 1023), all 1 bits in mantissa
double asDouble = *(double *)(&OnePointNineRepeating);
float asFloat = asDouble;
return asFloat;

編集、結果:元のポスターがこのテストを実行し、切り捨てにより、1.99999は/ arch:SSE2の有無にかかわらず2に切り上げられることがわかりました。

他のヒント

範囲の両端が含まれるように丸めを調整する場合、これらの極値は、極値以外の値の半分にしかならないでしょうか?

切り捨てでは、最大値を含めることはありません。

本当に最大値が必要ですか?文字通り、最大値に正確に到達する可能性はほぼゼロです。

とはいえ、精度をあきらめているという事実を利用して、次のようなことを行うことができます:

float MersenneFloat( float min, float max )
{
    double random = 100000.0; // just a dummy value
    while ((float)random > 65535.0)
    {
        //genrand returns a double in [1,2)
        double random = genrand_close1_open2() - 1.0; // now it's [0,1)
        random *= 65536.0; // now it's [0,65536). We try again if it's > 65535.0
    }
    //return in desired range
    return min + float(random/65535.0) * (max - min);
}

現在、MersenneFloatを呼び出すたびに、genrandを複数回呼び出す可能性がわずかにあります。したがって、閉じた間隔で可能なパフォーマンスを放棄しました。ダブルからフロートにダウンキャストしているため、精度を犠牲にすることはありません。

編集:アルゴリズムの改善

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top