2013-03-11 112 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