Эмпирическое правило для тестирования равенства два удваивается в C#?

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

Как я определяю то, что "разумный" означает?

Может double.Epsilon использоваться в некотором роде?


Обновление

Вещи пары. Как @ho1 указанный, документация для double.Epsilon указывает, что, когда дело доходит до сравнения два удваивается для равенства, Вы, вероятно, собираетесь хотеть значение, намного больше, чем эпсилон. Вот соответствующий абзац из документации:

Два по-видимому эквивалентных числа с плавающей запятой не могли бы выдержать сравнение равный из-за различий в их младших значащих цифрах. Например, выражение C#, (двойной) 1/3 == (дважды) 0.33333, не выдерживает сравнение равный, потому что операция деления на левой стороне имеет максимальную точность, в то время как константа на правой стороне точна только к указанным цифрам. При создании пользовательского алгоритма, который определяет, можно ли два числа с плавающей запятой считать равными, необходимо использовать значение, которое больше, чем Эпсилон, постоянный для установления приемлемого запаса по устойчивости различия для двух значений, которые будут считать равным. (Как правило, что поле различия много раз больше, чем Эпсилон.) - http://msdn.microsoft.com/en-us/library/system.double.epsilon.aspx

... но вопрос, сколько раз больше??

В случае, если это влияло бы на Ваш ответ, моя конкретная ситуация включает геометрические вычисления (такие как скалярные произведения и векторные произведения с помощью точек и векторов). В некоторых случаях Вы сделали различные выводы на основе ли A == B, A > B, или A < B, таким образом, я ищу хорошее эмпирическое правило для того, как определить размер эквивалентного окна.

14
задан devuxer 23 June 2010 в 17:47
поделиться

5 ответов

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

Подробности: Число с плавающей запятой по основанию 2 представлено как мантисса - число от 1 до 2 - умноженное на два в возведении в степень.Двойное значение имеет 52 бита для дробной части мантиссы плюс 11 бит точности для экспоненты. Если показатель степени является очень большим отрицательным значением, а мантисса равна 0, тогда вы получите значения, близкие к double.Epsilon , но если ваша экспонента достаточно большая, тогда даже очень небольшая разница в значениях двух значащих. приведет к значению, намного большему, чем double.Epsilon .

Полное обсуждение того, как проверить два числа с плавающей запятой на равенство, см. В «Сравнение чисел с плавающей запятой, издание 2012 г.» Брюса Доусона. Подводя итог, можно выделить три основных метода сравнения:

Использовать абсолютную разницу

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

Используйте относительную разницу

Примерно так:

if (Math.Abs(a - b) / b <= maxRelativeError)
{
    return true;
}

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

Использование единиц последнего места

Сравнение с использованием единиц последнего места (ULP) означает проверку последней части мантиссы. (В документе это называется «Сравнение с использованием целых чисел».) Это более сложный подход, но он очень надежен. В документе представлен исходный код на языке C; для C # вы, вероятно, могли бы использовать BitConverter.DoubleToInt64Bits .

В ответ на вашу правку

"Во сколько раз больше?" На самом деле это вопрос домена вашего приложения, и, вероятно, поэтому .NET Framework не предоставляет метод по умолчанию, но мне повезло, используя сравнение ULP с максимальной разницей ULP, равной 4.

18
ответ дан 1 December 2019 в 11:59
поделиться

Это немного зависит от того, с какими значениями вы работаете. Если вы работаете с числами, где вам важны только 2 знака после запятой, возможно, будет достаточно использовать 0.001. Иногда можно использовать Epsilon, но обычно я думаю, что нет.

Edit: Убрал ссылку на валюту, так как она отвлекает от сути.

Цитата из MSDN:

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

2
ответ дан 1 December 2019 в 11:59
поделиться
double d1 = GetRandomDouble();
double d2 = GetRandomDouble();

if (Math.Abs(d1 - d2) < double.Epsilon)
{
   // the doubles are equal
}

Обратите внимание, что на практике этот код эквивалентен просто d1 == d2, потому что эпсилон определяется как наименьшее возможное положительное значение > 0. Таким образом, у вас никогда не будет значения между 0 и эпсилоном, и если у вас есть такая ошибка округления/вычисления, которая вызовет проблемы с оператором ==, вы увидите ее и здесь.

Но что вы можете сделать, так это использовать эту технику для определения своего собственного уровня точности - своего собственного эпсилона. Я ожидал, что double.Equals() будет перегружен для этой техники, но документация ясно говорит, что его, как ни странно, не существует. Поэтому давайте создадим свой собственный:

public static bool IsEqual(this double d1, double d2, unsigned int precisionFactor)
{
   return Math.Abs(d1 - d2) < precisionFactor * double.Epsilon;
}
2
ответ дан 1 December 2019 в 11:59
поделиться

Поэтому я ищу хорошее эмпирическое правило для определения размера окна эквивалентности.

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

1
ответ дан 1 December 2019 в 11:59
поделиться

Вопрос в том, во сколько раз больше?

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

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

1
ответ дан 1 December 2019 в 11:59
поделиться
Другие вопросы по тегам:

Похожие вопросы: