Double.Epsilon для равенства, больше, меньше, меньше или равно, больше или равно

StackOverflow https://stackoverflow.com/questions/2411392

  •  18-09-2019
  •  | 
  •  

Вопрос

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

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

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

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

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

Это было полезно?

Решение

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

А System.Double type может представлять значения с точностью до 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 являются вычисленными значениями, то вам нужно увеличить эпсилон.

Другие советы

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

bool IsApproximatelyEqual(double value, double permittedVariance);

это выше моего понимания.

Я думаю, что соответствующие биты в опубликованной вами ссылке MSDN таковы:

Однако свойство Epsilon не является общей мерой точности двойного типа;Это применимо только к двойным экземплярам, ​​которые имеют ноль.

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

Это значение не определяется как наименьшее положительное число x, так что x + 1.0 не равно 1,0, поэтому двойной .epsilon нельзя использовать для «почти равенства».В рамках не существует постоянной константы, значение которого составляет наименьшее положительное число x, так что x + 1.0 не равно 1,0.

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

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

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

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

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

Вот код, который дважды был включен в набор средств управления 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.Вы захотите выбрать свой собственный эпсилон.

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

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

    while(one + result/2.0 != 1.0) {
        result/=2.0;
    }
    return result;
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top