Why does adding double.epsilon to a value result in the same value, perfectly equal?

I have a unit test, testing boundaries:

[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
}

I thought I'd just tip the 90.0 over the edge by adding the minimum possible positive double to it, but now the exception is not thrown, any idea why?

When debugging, I see top as coming in as 90, when it should be 90.00000000.... something.

EDIT: I should have thought a bit harder, 90+Double.Epsilon will lose its resolution. Seems the best way to go is do some bit shifting.

SOLUTION:

[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;
}

This does the job.

Jon Skeet
people
quotationmark

Double.Epsilon is the smallest positive representable value. Just because it's representable on its own does not mean it's the smallest value between any other representable value and the next highest one.

Imagine you had a system to represent just integers. You can represent any integer to 5 significant figures, along with a scale (e.g. in the range 1-100).

So these values are exactly representable, for example

  • 12345 (digits=12345, scale = 0)
  • 12345000 (digits=12345, scale = 3)

In that system, the "epsilon" value would be 1... but if you add 1 to 12345000 you'd still end up with 12345000 because the system couldn't represent the exact result of 12345001.

Now apply the same logic to double, with all its intricacies, and you get a much smaller epsilon, but the same general principle: a value which is distinct from zero, but still can end up not making any difference when added to larger numbers.

Note that much larger values have the same property too - for example, if x is a very large double, then x + 1 may well be equal to x because the gap between two "adjacent" doubles becomes more than 2 as the values get big.

people

See more on this question at Stackoverflow