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

spr*_*t12 21 c# double unit-testing epsilon double-precision

我有一个单元测试,测试边界:

[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
}
Run Code Online (Sandbox Code Playgroud)

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

在调试时,我看到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);
}

/// <summary>
/// Increment a double-precision number by the smallest amount possible
/// </summary>
/// <param name="number">double-precision number</param>
/// <returns>incremented number</returns>
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;
}

/// <summary>
/// Decrement a double-precision number by the smallest amount possible
/// </summary>
/// <param name="number">double-precision number</param>
/// <returns>decremented number</returns>
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;
}
Run Code Online (Sandbox Code Playgroud)

这样做了.

Jer*_*ert 26

根据以下文件Double.Epsilon:

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

(强调我的.)

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


Jon*_*eet 21

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.