簡體   English   中英

截斷時浮點舍入

[英]Floating point rounding when truncating

這可能是x86 FPU專家的一個問題:

我正在嘗試編寫一個函數,該函數生成[min,max]范圍內的隨機浮點值。 問題是我的生成器算法(浮點Mersenne Twister,如果你很好奇)只返回范圍[1,2]中的值 - 即,我想要一個包含上限,但我的“源”生成的值是從獨家上限。 這里的問題是底層生成器返回一個8字節的雙精度,但我只想要一個4字節的浮點數,而我正在使用Nearest的默認FPU舍入模式。

我想知道的是,在這種情況下,截斷本身是否會導致我的返回值包含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的截斷足以導致結果包含max。 有關更多信息,請參閱Crashworks的答案。

SSE操作將巧妙地改變該算法的行為,因為它們沒有中間的80位表示 - 數學真正以32位或64位完成。 好消息是,您可以通過簡單地為MSVC指定/ ARCH:SSE2命令行選項來輕松測試它並查看它是否會改變您的結果,這將導致它使用SSE標量操作而不是x87 FPU指令用於普通浮點數學。

我沒有確切的四舍五入行為周圍的整數界限什么肯定的副手,但你可以測試一下,看看會發生什么時1.999 ..會從64位到32位的四舍五入

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 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM