Подтвердить что ты не робот

Я пытаюсь понять код Microsoft DoubleUtil.AreClose(), над которым я размышлял

Если вы размышляете над 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));
}

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

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

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

4b9b3361

Ответ 1

  • Это, по-видимому, значение "допуска", основанное на величине сравниваемых чисел. Обратите внимание, что из-за того, что числа чисел с плавающей запятой представлены, наименьшая представимая разница между числами с показателем 0 равна 2 -53 или приблизительно 1.11022 × 10 -16. (См. блок на последнем месте и с плавающей запятой в Википедии.) Константа здесь ровно в два раза больше значение, поэтому оно допускает небольшие ошибки округления, накопленные во время вычислений.

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

Viz:.

return ((-num2 < num) && (num2 > num));
return ((num > -num2) && (num < num2));
return ((diff > -tolerance) && (diff < tolerance));

Ответ 2

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

/// <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

    // 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

Ответ 3

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

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

Ответ 4

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

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