为什么将double.epsilon添加到一个值会产生相同的值,完全相同?

我有一个unit testing,测试边界:

[TestMethod] [ExpectedException(typeof(ArgumentOutOfRangeException))] public void CreateExtent_InvalidTop_ShouldThrowArgumentOutOfRangeException() { var invalidTop = 90.0 + Double.Epsilon; new Extent(invalidTop, 0.0, 0.0, 0.0); } public static readonly double MAX_LAT = 90.0; public Extent(double top, double right, double bottom, double left) { if (top > GeoConstants.MAX_LAT) throw new ArgumentOutOfRangeException("top"); // not hit } 

我以为我只是通过添加最小可能的正双倍来向尖端90.0倾斜,但是现在没有抛出exception,任何想法为什么?

调试时,我看到top为90,当它应该是90.00000000 ….

编辑:我应该想一点, 90+Double.Epsilon将失去其分辨率。 似乎最好的方法是做一些转移。

解:

 [TestMethod] [ExpectedException(typeof(ArgumentOutOfRangeException))] public void CreateExtent_InvalidTop_ShouldThrowArgumentOutOfRangeException() { var invalidTop = Utility.IncrementTiny(90); // 90.000000000000014 // var sameAsEpsilon = Utility.IncrementTiny(0); new Extent(invalidTop, 0, 0, 0); } ///  /// Increment a double-precision number by the smallest amount possible ///  /// double-precision number /// incremented number public static double IncrementTiny(double number) { #region SANITY CHECKS if (Double.IsNaN(number) || Double.IsInfinity(number)) throw new ArgumentOutOfRangeException("number"); #endregion var bits = BitConverter.DoubleToInt64Bits(number); // if negative then go opposite way if (number > 0) return BitConverter.Int64BitsToDouble(bits + 1); else if (number < 0) return BitConverter.Int64BitsToDouble(bits - 1); else return Double.Epsilon; } ///  /// Decrement a double-precision number by the smallest amount possible ///  /// double-precision number /// decremented number public static double DecrementTiny(double number) { #region SANITY CHECKS if (Double.IsNaN(number) || Double.IsInfinity(number)) throw new ArgumentOutOfRangeException("number"); #endregion var bits = BitConverter.DoubleToInt64Bits(number); // if negative then go opposite way if (number > 0) return BitConverter.Int64BitsToDouble(bits - 1); else if (number < 0) return BitConverter.Int64BitsToDouble(bits + 1); else return 0 - Double.Epsilon; } 

这样做了。

根据Double.Epsilon的文档 :

Epsilon属性的值反映了Double实例的值为零时,在数值运算或比较中显着的最小正Double值。

(强调我的。)

将它添加到90.0不会产生“90.0之后的下一个最小值”,这只会再次产生90.0。

Double.Epsilon是最小的正可表示值。 仅仅因为它本身可以表示并不意味着它是任何其他可表示值和下一个最高值之间的最小值。

想象一下,你有一个系统来表示整数。 您可以将任意整数表示为5个有效数字,以及比例(例如,在1-100范围内)。

因此,例如,这些值可以准确表示

  • 12345(数字= 12345,比例= 0)
  • 12345000(数字= 12345,比例= 3)

在该系统中,“epsilon”值将为1 …但如果您添加1到12345000,您仍然会得到12345000,因为系统无法表示12345001的确切结果。

现在应用相同的逻辑double ,具有所有复杂性,并且你得到一个更小的epsilon,但是相同的一般原则:一个不同于零的值,但是当添加到更大的数字时仍然可以最终没有任何差别。

请注意,更大的值也具有相同的属性 – 例如,如果x是非常大的double ,则x + 1可能等于x因为两个“相邻”双精度之间的差距变得大于2,因为值变大。

因为Double.Epsilon是双重数字中“最小的显着变化”(松散地说)。

..但这并不意味着你使用它会产生任何影响。

如您所知,浮点数/双打的分辨率因其所含的vlue的大小而异。 例如,人工:

  • -100 – > + -0.1
  • -10 – > + -0.01
  • 0 – > + -0.001
  • 10 – > + -0.01
  • 100 – > + -0.1

如果分辨率是这样的,那么Epsilon将是0.001 ,因为这是最小的变化。 但是在这样的系统中1000000 + 0.001的预期结果是什么?

在C99和C ++中,执行您尝试执行的操作的函数称为nextafter ,并且在math.h 。 我不知道C#是否有任何等价物,但如果确实如此,我希望它有一个相似的名字。