簡體   English   中英

找到大於double值的最小浮點數

[英]Find minimum float greater than a double value

我有與CUDNN_BN_MIN_EPSILON值在被使用的問題cudnnBatchNormalizationForwardTraining功能(參見該文檔在這里 ),而且事實證明那是因為我是路過的float1e-5f而不是雙(我的工作float值保存內存並加快計算速度),這個值一旦轉換為float,就會略小於1e-5 ,這是該常量的實際值。

經過一些反復試驗,我發現我現在正在使用一個不錯的近似值:

const float CUDNN_BN_MIN_EPSILON = 1e-5f + 5e-13f;

我確信有更好的方法可以解決這樣的問題,所以問題是:

給定一個正的double值,找到最小可能float值的最佳(如“可靠”)方式是什么(單獨使用以及if / when轉換為double )是否嚴格大於初始double值?

另一種形成這個問題的方法是,給定doubled1floatf1d1 - (float)f1應該是最小可能的負值 (否則它意味着f1 小於 d1,這不是我們正在尋找什么)。

我做了一些基本的試驗和錯誤(使用1e-5作為我的目標值):

// Check the initial difference
> 1e-5 - 1e-5f
2,5262124918247909E-13 // We'd like a small negative value here

// Try to add the difference to the float value
> 1e-5 - (1e-5f + (float)(1e-5 - 1e-5f))
2,5262124918247909E-13 // Same, probably due to approximation

// Double the difference (as a test)
> 1e-5 - (1e-5f + (float)((1e-5 - 1e-5f) * 2))
-6,5687345259044915E-13 // OK

使用此近似值,最終float值為1,00000007E-05 ,看起來很好。

但是* 2乘法在我的結尾完全是任意的,我不確定它是可靠的還是在那里做的最佳可能的事情。

有沒有更好的方法來實現這一目標?

謝謝!


編輯 :這是我現在使用的(壞)解決方案,很樂意用更好的解決方案替換它!

/// <summary>
/// Returns the minimum possible upper <see cref="float"/> approximation of the given <see cref="double"/> value
/// </summary>
/// <param name="value">The value to approximate</param>
public static float ToApproximatedFloat(this double value)
    => (float)value + (float)((value - (float)value) * 2);

解決方案 :這是最終正確的實施(感謝John Bollinger):

public static unsafe float ToApproximatedFloat(this double value)
{
    // Obtain the bit representation of the double value
    ulong bits = *((ulong*)&value);

    // Extract and re-bias the exponent field
    ulong exponent = ((bits >> 52) & 0x7FF) - 1023 + 127;

    // Extract the significand bits and truncate the excess
    ulong significand = (bits >> 29) & 0x7FFFFF;

    // Assemble the result in 32-bit unsigned integer format, then add 1
    ulong converted = (((bits >> 32) & 0x80000000u)
                        | (exponent << 23)
                        | significand) + 1;

    // Reinterpret the bit pattern as a float
    return *((float*)&converted);
}

在C:

#include <math.h>

float NextFloatGreaterThan(double x)
{
    float y = x;
    if (y <= x) y = nexttowardf(y, INFINITY);
    return y;
}

如果你不想使用庫例程,那么用-NextBefore(-y)替換nexttowardf(y, INFINITY) -NextBefore(-y) ,其中NextBefore取自這個答案並修改:

  • 更改doublefloatDBL_FLT_
  • .625更改為.625f
  • SmallestPositive < fabs(q)*Scale ? fabs(q)*Scale : SmallestPositive替換fmax(SmallestPositive, fabs(q)*Scale) SmallestPositive < fabs(q)*Scale ? fabs(q)*Scale : SmallestPositive SmallestPositive < fabs(q)*Scale ? fabs(q)*Scale : SmallestPositive
  • (q < 0 ? -q : q)替換fabs(q) (q < 0 ? -q : q)

(顯然,例程可以從-NextBefore(-y)轉換為NextAfter(y) 。這留給讀者練習。)

因為您似乎對表示級別的詳細信息感興趣,所以您將依賴於floatdouble類型的表示。 然而,在實踐中,很可能歸結為IEEE-754的基本“binary32”和“binary64”格式。 它們具有一個符號位的一般形式,幾個偏置指數位和一組有效位,包括對於歸一化值,一個有效位的隱含位。

簡單的案例

鑒於IEEE-754二進制64格式的double ,其值不低於+ 2-126 ,您想要做的是

  • 以可以直接檢查和操作的形式獲得原始double值的位模式。 例如,作為無符號的64位整數。

     double d = 1e-5; uint64_t bits; memcpy(&bits, &d, 8); 
  • 提取並重新偏置指數字段

     uint64_t exponent = ((bits >> 52) & 0x7FF) - 1023 + 127; 
  • 提取有效位並截斷多余的位

     uint64_t significand = (bits >> 29) & 0x7fffff; 
  • 以32位無符號整數格式匯總結果

     uint32_t float_bits = ((bits >> 32) & 0x80000000u) | (exponent << 23) | significand; 
  • 加一個。 由於您希望結果嚴格大於原始double ,因此無論所有截斷的有效位數是否為0,這都是正確的。如果加法溢出有效位,則將正確遞增指數字段。 然而,它可能產生無窮大的位模式。

     float_bits += 1; 
  • 將位模式存儲/復制/重新解釋為float

     float f; memcpy(&f, &float_bits, 4); 

負數

給定binary64格式的負double ,其幅度不小於2 -126 ,遵循上述過程,除了從float_bits減1而不是添加1。 請注意,對於-2 -126 ,這會產生一個二次正常的二進制32(見下文),這是正確的結果。

零和非常小的數字,包括次正規數

IEEE 754提供了非零數量非零數量的精度低的表示。 這種表示稱為次正規 在某些情況下,超過給定輸入binary64的最小二進制32是一個次正規,包括一些不是二進制64次正規的輸入。

此外,IEEE 754提供帶符號的零,-0是一種特殊情況:嚴格大於-0(任一格式)的最小二進制32是最小的正次正規數。 注意:不是+0,因為根據IEEE 754,+ 0和-0通過正常的比較運算符進行比較。 最小正,非零,次正規binary32值具有位模式0x00000001。

受這些考慮因素影響的二進制64值具有偏置的二進制64指數字段,其值小於或等於二進制64指數偏差和二進制32指數偏差之間的差值(896)。 這包括具有精確為0的有偏差指數的那些,其表征二進制64零和子正規。 在簡單案例程序中檢查重組步驟應該使您正確地得出結論,該程序將對這些輸入產生錯誤的結果。

這些案件的代碼留作練習。

無窮大和NaN

具有偏置的二進制64指數字段集的所有位的輸入表示正或負無窮大(當binary64有效數字沒有設置位時)或非數字(NaN)值。 Binary64 NaNs和正無窮大應該轉換為它們的binary32當量。 負無窮大可能應該轉換為最大量級的負二進制32值。 這些需要作為特殊情況處理。

這些案件的代碼留作練習。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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