2009-03-13 38 views
3

を切り捨てたときにこれはおそらくの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の最適化のどのバージョンを使用してもこれに対する答えが変わるでしょうか?

編集:答えは「はい」です。この場合、切り捨てが2倍から浮動小数点になり、結果に最大値を含めるには十分です。詳しくは、Crashworksの回答を参照してください。

答えて

4

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

正確な丸めの振る舞いが整数境界の周りにあるのはわかりませんが、1.999のときに何が起こるかをテストすることができます。元のポスターは、このテストを実行し、切り捨てと、1.99999を有する及び/アーチなしの両方2に切り上げるであろうことがわかっ:SSE2 例えば

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; 

を編集し、その結果、64ビットから32ビットに丸められます。

+0

なぜ私は走った他の人の間でこのテストを実行すると思いませんでしたか?私は切り捨てで、1.99999は/ arch:SSE2の有無にかかわらず2に丸めます。ありがとう! –

+0

喜んで助けてください - 私はテストの結果が自分自身であるかどうか不思議でした。 – Crashworks

0

範囲の両端を含むように丸めを調整すると、これらの極値は極端でないものの半分になることはありませんか?

+0

私はちょうど切り捨てを使用する場合、答えははいですが、私は最大有効数字をインクリメントした場合、答えはノーとなります。 –

0

切り捨てでは、決して最大値を含むことはありません。

本当に最大値が必要ですか?文字通り、最大値に正確に着陸するチャンスはほとんどありません。今、それはあなたが呼び出すたびにgenrandするために複数の呼び出しのわずかなチャンスを持っている、ということ

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。だから、あなたは閉じられた間隔のために可能なパフォーマンスをあきらめました。あなたがダブルからフロートにダウンキャストしているので、あなたは精度を犠牲にすることになります。

編集: - 数学が本当に32または64ビットで行われ、彼らは中間80ビット表現を持っていないので、改善されたアルゴリズム

+0

はい、私はmaxをインクルーシブにする必要があります(ライブラリ関数の契約です)。乗算の前に私の最大値の仮数を増やすのではなく、あなたのやり方に何か利点がありますか? –

+0

それでもうまくいくかもしれません。しかし、どこかで、拒否テストを行う必要があるか、完全ではない値の分布を持つことになります。 この問題のアナログは、たとえば、0〜65535のランダムな整数から0〜256の整数を生成することです。それは単に均等にマップされません。 – rlbond

+0

実際に、私はCrashworksのテスト提案を試みましたが、切り詰めは実際には丸めます。 –

関連する問題