Я пытаюсь понять код 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 (в качестве примера).
Ответы
Ответ 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)) < 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 не будут достаточно близко:)