获得下一个最小的双号

作为unit testing的一部分,我需要测试一些边界条件。 一种方法接受System.Double参数。

有没有办法获得下一个最小的双值? (即将尾数减1个单位值)?

我考虑使用Double.Epsilon但这是不可靠的,因为它只是从零开始的最小增量,因此不适用于较大的值(即9999999999 - Double.Epsilon == 9999999999 )。

那么所需的算法或代码是什么:

 NextSmallest(Double d) < d 

……总是如此。

如果您的数字是有限的,您可以在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格式,使得构成指数和尾数的位一起形成一个与浮点数具有相同排序的整数。 因此,要获得最大的较小数字,如果值为正数,则可以从此数字中减去1,如果值为负,则可以添加1。

这有效的关键原因是没有存储尾数的前导位。 如果你的尾数都是零,那么你的数字是2的幂。 如果从指数/尾数组合中减去1,则得到所有1并且您将不得不从指数位借用。 换句话说:你必须减少指数,这正是我们想要的。

关于双精度浮点的维基百科页面在这里: http : //en.wikipedia.org/wiki/Double_precision_floating-point_format

为了好玩,我写了一些代码来打破double格式的二进制表示,减少尾数并重新组合结果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,并且如上所示存在实际差异:)