Double.Epsilon for equality, greater than, less th

2019-01-02 15:33发布

问题:

http://msdn.microsoft.com/en-us/library/system.double.epsilon.aspx

If you create a custom algorithm that determines whether two floating-point numbers can be considered equal, you must use a value that is greater than the Epsilon constant to establish the acceptable absolute margin of difference for the two values to be considered equal. (Typically, that margin of difference is many times greater than Epsilon.)

So is this not really an epsilon that could be used for comparisons? I don't really understand the MSDN wording.

Can it be used as the epsilon in the examples here? - What is the most effective way for float and double comparison?

And finally this seems really important so I'd like to make sure I have a solid implementation for equality, greater than, less than, less than or equal to, and greater than or equal to.

回答1:

I don't know what they were smoking when they wrote that. Double.Epsilon is the smallest representable non-denormal floating point value that isn't 0. All you know is that, if there's a truncation error, it will always be larger than this value. Much larger.

The System.Double type can represent values accurate to up to 15 digits. So a simple first order estimate if a double value x is equal to some constant is to use an epsilon of constant * 1E-15

public static bool AboutEqual(double x, double y) {
    double epsilon = Math.Max(Math.Abs(x), Math.Abs(y)) * 1E-15;
    return Math.Abs(x - y) <= epsilon;
}

You have to watch out though, truncation errors can accumulate. If both x and y are computed values then you have to increase the epsilon.



回答2:

I'd like to make sure I have a solid implementation for equality, greater than, less than, less than or equal to, and greater than or equal to.

You are using binary floating point arithmetic.

Binary floating point arithmetic was designed to represent physical quantities like length, mass, charge, time, and so on.

Presumably then you are using binary floating point arithmetic as it was intended to be used: to do arithmetic on physical quantities.

Measurements of physical quantities always have a particular precision, depending on the precision of the device used to measure them.

Since you are the one providing the values for the quantities you are manipulating, you are the one who knows what the "error bars" are on that quantity. For example, if you are providing the quantity "the height of the building is 123.56 metres" then you know that this is accurate to the centimetre, but not to the micrometer.

Therefore, when comparing two quantities for equality, the desired semantics is to say "are these two quantities equal within the error bars specified by each measurement?"

So now we have an answer to your question. What you must do is keep track of what the error is on each quantity; for example, the height of the building is "within 0.01 of 123.56 meters" because you know that is how precise the measurement is. If you then get another measurement which is 123.5587 and want to know whether the two measurements are "equal" within error tolerances, then do the subtraction and see if it falls into the error tolerance. In this case it does. If the measurements were in fact precise to the micrometre, then they are not equal.

In short: you are the only person here who knows what sensible error tolerances are, because you are the only person who knows where the figures you are manipulating came from in the first place. Use whatever error tolerance makes sense for your measurements given the precision of the equipment you used to produce it.



回答3:

If you have two double values that are close to 1.0, but they differ in only their least significant bits, then the difference between them will be many orders of magnitude greater than Double.Epsilon. In fact, the difference is 324 decimal orders of magnitude. This is because of the effect of the exponent portion. Double.Epsilon has a huge negative exponent on it, while 1.0 has an exponent of zero (after the biases are removed, of course).

If you want to compare two similar values for equality, then you will need to choose a custom epsilon value that is appropriate for the orders-of-magnitude size of the values to be compared.

If the double values that you are comparing are near 1.0. Then the value of the least siginificant bit would be near 0.0000000000000001. If the double values that you are comparing are in the quadrillions, then the value of the least significant bit could be as much as a thousand. No single value for epsilon could be used for equality comparisons in both of those circumstances.



回答4:

I just did this - using Kent Bogarts idea.

private bool IsApproximatelyEqual(double x, double y, double acceptableVariance)
{
     double variance = x > y ? x - y : y - x;
     return variance < acceptableVariance;

     //or
     //return Math.Abs(x - y) < acceptableVariance;
}


回答5:

It could be used for comparisons, assuming you want to ensure the two values are either exactly equal, or have the smallest representable difference for the double type. Generally speaking, you would want to use a number greater than double.Epsilon to check whether two doubles are approximately equal.

Why the .NET framework doesn't define something like

bool IsApproximatelyEqual(double value, double permittedVariance);

is beyond me.



回答6:

I think the pertinent bits in the MSDN link you posted are these:

However, the Epsilon property is not a general measure of precision of the Double type; it applies only to Double instances that have a value of zero.

Note: The value of the Epsilon property is not equivalent to machine epsilon, which represents the upper bound of the relative error due to rounding in floating-point arithmetic.

This value is not defined as smallest positive number x, such that x + 1.0 is not equal to 1.0, so Double.Epsilon cannot be used for "almost equality". There exists no constant in the framework whose value is smallest positive number x, such that x + 1.0 is not equal to 1.0.

I have to say, that surprises me. I too had assumed that Double.Epsilon was the equivalent of DBL_EPSILON in c/c++ - clearly not!

From what I can read of that link it seems to be saying 'you need to figure out a decent value yourself for comparisons' which is rather surprising to say the least.
Perhaps someone more knowledgable can clarify :)



回答7:

I use the following

public static class MathUtil {
    /// <summary>
    /// smallest such that 1.0+EpsilonF != 1.0
    /// </summary>
    public const float EpsilonF = 1.192092896e-07F;

    /// <summary>
    /// smallest such that 1.0+EpsilonD != 1.0
    /// </summary>
    public const double EpsilonD = 2.2204460492503131e-016;

    [MethodImpl( MethodImplOptions.AggressiveInlining )]
    public static bool IsZero( this double value ) {
        return value < EpsilonD && value > -EpsilonD;
    }

    [MethodImpl( MethodImplOptions.AggressiveInlining )]
    public static int Sign( this double value ) {
        if ( value < -EpsilonD ) {
            return -1;
        }
        if ( value > EpsilonD )
            return 1;
        return 0;
    }

and if you want to check for equality of two doubles 'a' and 'b', you can use

(a-b).IsZero();

and if you want to get the comparison result, use

(a-b).Sign();


回答8:

The problem with comparing doubles is that when you do a comparison between two different math results that are equal but which, due to rounding errors, aren't evaluating to the same value, they will have some difference...which is larger than epsilon, except on edge cases. And using a reliable epsilon value is also difficult. Some people consider two doubles equal if the difference between them is less than some percentage value, since using a static minimum difference epsilon may mean your differences are too small or large when the double itself is high or low.



回答9:

Here's some code that included twice within the Silverlight Control Toolkit:

    public static bool AreClose(double value1, double value2)
    {
        //in case they are Infinities (then epsilon check does not work)
        if(value1 == value2) return true;
        // This computes (|value1-value2| / (|value1| + |value2| + 10.0)) < DBL_EPSILON
        double eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * DBL_EPSILON;
        double delta = value1 - value2;
        return(-eps < delta) && (eps > delta);
    }

In one place they use 1e-6 for epsilon; in another they use 1.192093E-07. You will want to choose your own epsilon.



回答10:

There is no choice you have to calculate it yourself or define own constant.

double calculateMachineEpsilon() {
    double result = 1.0;
    double one = 1.0/256;

    while(one + result/2.0 != 1.0) {
        result/=2.0;
    }
    return result;
}


标签: