2013-03-11 72 views
21

作爲單元測試的一部分,我需要測試一些邊界條件。一種方法接受System.Double參數。獲取下一個最小的雙數

有沒有辦法取得次小的雙重價值? (即將尾數減1個單位值)?

我認爲是使用Double.Epsilon,但這是不可靠的,因爲它只是從零開始的最小增量,所以不適用於較大的值(即9999999999 - Double.Epsilon == 9999999999)。

那麼,什麼是算法或代碼所需要這樣的:

NextSmallest(Double d) < d 

...總是正確的。

+0

如果你除以10 – Hogan 2013-03-11 03:22:03

+4

我覺得你的問題已經在這裏回答:http://stackoverflow.com/a/2283565/1715579。 – 2013-03-11 03:22:37

回答

14

如果您的號碼是有限的,你可以使用幾個方便的方法在BitConverter類的實際區別:

long bits = BitConverter.DoubleToInt64Bits(value); 
if (value > 0) 
    return BitConverter.Int64BitsToDouble(bits - 1); 
else if (value < 0) 
    return BitConverter.Int64BitsToDouble(bits + 1); 
else 
    return -double.Epsilon; 

IEEE -754格式被設計成使得構成指數和尾數的位形成一個與浮點數具有相同排序的整數。因此,要獲得最大的較小數字,如果該值爲正數,則可以從該數字中減去一個數字,如果該值爲負數,則可以添加一個數字。

這項工作的關鍵原因是尾數的前導位未被存儲。如果你的尾數全爲零,那麼你的數字是2的冪。如果從指數/尾數組合中減去1,則得到全部1,並且您必須從指數位借用。換句話說:你必須減少指數,這正是我們想要的。

+0

謝謝,我結束了使用這種方法,它對我有用 – Dai 2013-03-19 07:55:48

+0

那麼'NaN'怎麼樣?我們不需要一個特例爲他們? – m93a 2018-01-19 20:49:53

3

雙精度浮點的維基百科頁面是在這裏:http://en.wikipedia.org/wiki/Double_precision_floating-point_format

爲了好玩,我寫了一些代碼,以擺脫double格式的二進制表示,遞減尾數和重新組成產生的兩倍。由於尾數隱含,我們必須檢查它並相應地修改指數,並且可能會在極限附近失效。

下面的代碼:

public static double PrevDouble(double src) 
{ 
    // check for special values: 
    if (double.IsInfinity(src) || double.IsNaN(src)) 
     return src; 
    if (src == 0) 
     return -double.MinValue; 

    // get bytes from double 
    byte[] srcbytes = System.BitConverter.GetBytes(src); 

    // extract components 
    byte sign = (byte)(srcbytes[7] & 0x80); 
    ulong exp = ((((ulong)srcbytes[7]) & 0x7F) << 4) + (((ulong)srcbytes[6] >> 4) & 0x0F); 
    ulong mant = ((ulong)1 << 52) | (((ulong)srcbytes[6] & 0x0F) << 48) | (((ulong)srcbytes[5]) << 40) | (((ulong)srcbytes[4]) << 32) | (((ulong)srcbytes[3]) << 24) | (((ulong)srcbytes[2]) << 16) | (((ulong)srcbytes[1]) << 8) | ((ulong)srcbytes[0]); 

    // decrement mantissa 
    --mant; 

    // check if implied bit has been removed and shift if so 
    if ((mant & ((ulong)1 << 52)) == 0) 
    { 
     mant <<= 1; 
     exp--; 
    } 

    // build byte representation of modified value 
    byte[] bytes = new byte[8]; 
    bytes[7] = (byte)((ulong)sign | ((exp >> 4) & 0x7F)); 
    bytes[6] = (byte)((((ulong)exp & 0x0F) << 4) | ((mant >> 48) & 0x0F)); 
    bytes[5] = (byte)((mant >> 40) & 0xFF); 
    bytes[4] = (byte)((mant >> 32) & 0xFF); 
    bytes[3] = (byte)((mant >> 24) & 0xFF); 
    bytes[2] = (byte)((mant >> 16) & 0xFF); 
    bytes[1] = (byte)((mant >> 8) & 0xFF); 
    bytes[0] = (byte)(mant & 0xFF); 

    // convert back to double and return 
    double res = System.BitConverter.ToDouble(bytes, 0); 
    return res; 
} 

所有這一切都爲您提供了一個值是由在尾數最低位發生變化的初始值不同......在理論上:)

這是一個測試:

public static Main(string[] args) 
{ 
    double test = 1.0/3; 
    double prev = PrevDouble(test); 
    Console.WriteLine("{0:r}, {1:r}, {2:r}", test, prev, test - prev); 
} 

給我的電腦上,結果如下:

0.33333333333333331, 0.33333333333333326, 5.5511151231257827E-17 

區別在那裏,但可能低於舍入閾值。表達test == prev的值爲false不過,並沒有如上圖所示:)

+2

我建議改變你的格式字符串爲「{0:r},{1:r},{2:r}」'來打印出「雙向」格式的雙打,這將顯示不同之處。它打印:「0.33333333333333331,0.333333333333333326,5。5511151231257827E-17「 – porges 2013-03-11 05:26:29

+0

謝謝@Porges,我會修改答案:) – Corey 2013-03-11 05:37:07