Я пытаюсь понять код Microsoft DoubleUtil.AreClose (), над которым я размышлял - PullRequest
13 голосов
/ 22 апреля 2011

Если вы поразмышляете над WindowsBase.dll > MS.Internal.DoubleUtil.AreClose(...), вы получите следующий код:

public static bool AreClose(double value1, double value2)
{
    if (value1 == value2)
    {
        return true;
    }
    double num2 = ((Math.Abs(value1) + Math.Abs(value2)) + 10.0) * 2.2204460492503131E-16;
    double num = value1 - value2;
    return ((-num2 < num) && (num2 > num));
}

Я пытаюсь понять две разные вещи:

  1. Гдеони придумали формулу для num2?Полагаю, я просто не понимаю значимости первого добавления значения 10.0 и второго умножения всех результатов на это число 2.2204460492503131E-16.Кто-нибудь знает, почему используется эта формула?

  2. Какой смысл в выражении return?Кажется, что по умолчанию, если num2 больше чем num, чем отрицательное значение num2 должно быть меньше чем num.Может быть, я что-то здесь упускаю, но это кажется излишним.Для меня это все равно, что проверить, больше ли 5, чем 3, и если -5 меньше 3 (в качестве примера).

Ответы [ 4 ]

13 голосов
/ 22 апреля 2011
  1. Похоже, что это значение "допуска", основанное на величине сравниваемых чисел. Обратите внимание, что из-за того, как представлены числа с плавающей запятой, наименьшая представимая разница между числами с показателем 0 равна 2 -53 или приблизительно 1,11022 × 10 -16 . (См. единицу в последнем месте и с плавающей точкой в Википедии.) Здесь константа ровно в два раза превышает это значение, поэтому она допускает небольшие ошибки округления, которые накопились во время вычислений.

  2. Если переупорядочить параметры в условных выражениях, а затем переименовать num2 в tolerance и num в diff, это должно стать ясным.

Viz:.

return ((-num2 < num) && (num2 > num));
return ((num > -num2) && (num < num2));
return ((diff > -tolerance) && (diff < tolerance));
4 голосов
/ 22 апреля 2011

Комментарии должны помочь понять этот метод:)

/// <summary>
/// AreClose - Returns whether or not two doubles are "close".  That is, whether or 
/// not they are within epsilon of each other.  Note that this epsilon is proportional
/// to the numbers themselves to that AreClose survives scalar multiplication.
/// There are plenty of ways for this to return false even for numbers which
/// are theoretically identical, so no code calling this should fail to work if this 
/// returns false.  This is important enough to repeat:
/// NB: NO CODE CALLING THIS FUNCTION SHOULD DEPEND ON ACCURATE RESULTS - this should be
/// used for optimizations *only*.
/// </summary>
/// <returns>
/// bool - the result of the AreClose comparision.
/// </returns>
/// <param name="value1"> The first double to compare. </param>
/// <param name="value2"> The second double to compare. </param>
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)) &lt; DBL_EPSILON
    double eps = (Math.Abs(value1) + Math.Abs(value2) + 10.0) * DBL_EPSILON;
    double delta = value1 - value2;
    return (-eps < delta) && (eps > delta);
}

Обновление

А здесь "мистическое" значение DBL_EPSILON

<code>    // Const values come from sdk\inc\crt\float.h
    internal const double DBL_EPSILON = 2.2204460492503131e-016; /* smallest such that 1.0+DBL_EPSILON != 1.0 */

src

2 голосов
/ 22 апреля 2011

Поиск в Google по этому номеру приведет меня на эту страницу http://en.m.wikipedia.org/wiki/Machine_epsilon

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

0 голосов
/ 22 апреля 2011

Не знаю почему, но чем ближе цифры к 0, тем меньше разница, чтобы пройти проверку.

И для небольших чисел возврат имеет смысл, например, принимать значения 0 и 1. Без первой части она прошла бы, но 0 и 1 недостаточно близко:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...