Double.Epsilon для равенства, больше, меньше, меньше или равно, больше или равно - PullRequest
51 голосов
/ 09 марта 2010

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

Если вы создаете собственный алгоритм, который определяет, будут ли два числа с плавающей точкой числа можно считать равными, вы должен использовать значение, которое больше, чем константа Эпсилона для установления приемлемый абсолютный запас Разница для двух значений будет считается равным. (Как правило, это разница в размерах многократно больше, чем эпсилон.)

Так это не эпсилон, который можно использовать для сравнения? Я не очень понимаю формулировку MSDN.

Можно ли использовать его в качестве эпсилона в примерах здесь? - Какой самый эффективный способ сравнения с плавающей запятой и двойного сравнения?

И, наконец, это кажется действительно важным, поэтому я хотел бы убедиться, что у меня есть надежная реализация для равенства, больше, меньше, меньше или равно и больше или равно.

Ответы [ 10 ]

75 голосов
/ 09 марта 2010

Я не знаю что они курили, когда писали это. Double.Epsilon - это наименьшее представимое неденормированное значение с плавающей запятой, которое не равно 0. Все, что вы знаете, это то, что при ошибке усечения оно всегда будет больше , чем это значение. Гораздо больше.

Тип System.Double может представлять значения с точностью до 15 цифр. Таким образом, простая оценка первого порядка, если двойное значение x равно некоторой константе, состоит в использовании эпсилона постоянной * 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;
}

Вы должны остерегаться, ошибки усечения могут накапливаться. Если и x, и y являются вычисленными значениями, то вам нужно увеличить эпсилон.

45 голосов
/ 09 марта 2010

Я бы хотел убедиться, что у меня есть надежная реализация для равенства: больше, меньше, меньше или равно и больше или равно.

Вы используете двоичную арифметику с плавающей запятой.

Двоичная арифметика с плавающей запятой была разработана для представления физических величин, таких как длина, масса, заряд, время и т. Д.

Предположительно, тогда вы используете двоичную арифметику с плавающей запятой, как и было задумано: для выполнения арифметики по физическим величинам.

Измерения физических величин всегда имеют особую точность, в зависимости от точности устройства, используемого для их измерения.

Поскольку вы - тот, кто предоставляет значения для величин, которыми вы манипулируете, вы - тот, кто знает, каковы «погрешности» для этого количества. Например, если вы указываете величину «высота здания составляет 123,56 метра», то вы знаете, что это с точностью до сантиметра, а не микрометра.

Следовательно, при сравнении двух величин на равенство желаемая семантика должна сказать: «Являются ли эти две величины равными в пределах погрешностей, определяемых каждым измерением?»

Так что теперь у нас есть ответ на ваш вопрос. То, что вы должны сделать, это отслеживать, какая ошибка есть для каждой величины; например, высота здания «в пределах 0,01 от 123,56 метра», потому что вы знаете, насколько точны измерения. Если затем вы получите другое измерение, которое является 123.5587, и хотите узнать, являются ли два измерения "равными" в пределах допустимых отклонений, выполните вычитание и посмотрите, попадает ли оно в допустимое отклонение. В этом случае это так. Если измерения были на самом деле с точностью до микрометра, то они не равны.

Короче говоря: вы единственный здесь, кто знает, что такое допустимые погрешности, потому что вы единственный человек, который знает, откуда появились цифры, которыми вы манипулируете. Используйте любую погрешность, которая имеет смысл для ваших измерений, учитывая точность оборудования, которое вы использовали для его производства.

9 голосов
/ 09 марта 2010

Если у вас есть два двойных значения, близких к 1,0, но они отличаются только своими младшими значащими битами, тогда разница между ними будет на много порядков больше, чем Double.Epsilon. На самом деле, разница составляет 324 десятичных порядка. Это из-за эффекта экспоненциальной части. Double.Epsilon имеет огромный отрицательный показатель степени, в то время как 1.0 имеет показатель нулевой степени (конечно, после устранения смещений).

Если вы хотите сравнить два одинаковых значения на равенство, вам нужно будет выбрать собственное значение эпсилона, соответствующее размеру порядка сравниваемых значений.

Если сравниваемые двойные значения близки к 1,0. Тогда значение младшего значащего бита будет около 0,0000000000000001. Если сравниваемые вами двойные значения находятся в квадриллионах, то значение младшего значащего бита может достигать тысячи. Ни одно значение для эпсилона не может быть использовано для сравнения равенства в обоих этих обстоятельствах.

6 голосов
/ 05 января 2012

Я только что сделал это - используя идею Кента Богарта.

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;
}
4 голосов
/ 09 марта 2010

Может использоваться для сравнений, если вы хотите убедиться, что два значения либо точно равны, либо имеют наименьшее представимое различие для типа double. Вообще говоря, вы хотели бы использовать число, большее double.Epsilon, чтобы проверить, равны ли два двойных числа.

Почему .NET Framework не определяет что-то вроде

bool IsApproximatelyEqual(double value, double permittedVariance);

вне меня.

3 голосов
/ 09 марта 2010

Я думаю, что подходящие биты в ссылке MSDN, которую вы разместили, это:

Однако свойство Epsilon не является общая мера точности Двойной тип; это относится только к Double экземпляры со значением ноль.

Примечание: стоимость Эпсилона свойство не эквивалентно машине эпсилон, который представляет верхнюю Граница относительной ошибки из-за округление в арифметике с плавающей точкой.

Это значение не определено как наименьшее положительное число х, такое что х + 1,0 не равно 1,0, поэтому Double.Epsilon не может использоваться для "почти равенства". Там нет константы в рамки, значение которых наименьшее положительное число х, такое что х + 1,0 не равно 1,0.

Должен сказать, это меня удивляет. Я тоже предполагал, что Double.Epsilon был эквивалентом DBL_EPSILON в c / c ++ - явно нет!

Из того, что я могу прочитать об этой ссылке, можно сказать, что «вам нужно самим найти достойную ценность для сравнений», что довольно удивительно, если не сказать больше.
Возможно, кто-то более знающий может уточнить:)

2 голосов
/ 11 октября 2017

Я использую следующее

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

и если вы хотите проверить на равенство двух двойных 'a' и 'b', вы можете использовать

(a-b).IsZero();

и если вы хотите получить результат сравнения, используйте

(a-b).Sign();
1 голос
/ 09 марта 2010

Проблема со сравнением значений типа double состоит в том, что при сравнении двух разных математических результатов, которые равны, но которые из-за ошибок округления не оценивают одно и то же значение, они будут иметь некоторую разницу ... больше, чем эпсилон, за исключением крайних случаев. И использование надежного значения эпсилон также сложно. Некоторые люди считают, что две двойные равны, если разница между ними меньше некоторого процентного значения, так как использование статической минимальной разницы эпсилон может означать, что ваши различия слишком малы или велики, когда сам двойник высок или низок.

0 голосов
/ 12 августа 2014

Нет выбора, вы должны рассчитать его самостоятельно или определить собственную константу.

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

    while(one + result/2.0 != 1.0) {
        result/=2.0;
    }
    return result;
}
0 голосов
/ 09 марта 2010

Вот код, который дважды был включен в набор инструментов управления Silverlight:

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

В одном месте они используют 1e-6 для эпсилон; в другом они используют 1.192093E-07. Вы захотите выбрать свой собственный эпсилон.

...