В чем разница между DBL_EPSILON и DBL_MIN? - PullRequest
0 голосов
/ 01 апреля 2020

Я хочу судить, равны ли два числа с плавающей точкой. Нормальная операция должна быть fabs (a - b) < DBL_EPSILON, но когда a = pow (2, -100), b = pow (2, -101), результат сравнения будет true ,, поскольку результат (a - b) равен 3.9443e-31, меньше DBL_EPSILON = 2.22045e-16. На самом деле эти два числа не равны, a в два раза больше, чем b. Если я сравню их с DBL_MIN, они действительно не равны. Должен ли я сделать это?

определение в вики:
DBL_MIN - минимальное нормализованное положительное значение double;
DBL_EPSILON - разница между 1.0 и следующим представимым значением double .
Согласно приведенному выше определению, DBL_EPSILON - это минимальная точность двойного значения, почему существует DBL_MIN? Какая связь между DBL_MIN и DBL_EPSILON?

Ответы [ 2 ]

0 голосов
/ 03 апреля 2020

Я хочу судить, равны ли два числа с плавающей точкой.

Нет, вы этого не делаете. Что вы на самом деле пытаетесь сделать, это проверить, равны ли два действительных числа a и b , когда у вас есть только два числа a и b, где a и b являются результатами операций с плавающей точкой, но a и b являются результатами математики действительных чисел.

Два объекта с плавающей точкой сравниваются равными, если и только если они представляют равные числа. Итак, если вы пытаетесь определить, равны ли два числа с плавающей запятой, все, что нужно, - это оценить a == b Это оценивается как истина, если и только если a и b равны. Таким образом, «сравнение чисел с плавающей точкой» легко. Но вы хотите «сравнить два действительных числа, которые были бы у меня, если бы я использовал арифметику действительных чисел c, но у меня были только числа с плавающей запятой», и это нелегко.

Нормальная операция должна быть fabs (a - b)

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

определение в вики:

DBL_MIN - минимальное нормализованное положительное значение double;

DBL_EPSILON - разница между 1,0 и следующим представимым значением double.

Согласно определению, приведенному выше, DBL_EPSILON является минимальной точностью двойного значения зачем там DBL_MIN? Какая связь между DBL_MIN и DBL_EPSILON?

В вашем вопросе не указано, какой язык программирования или реализацию вы используете, поэтому мы не знаем точно, что используется для типа double, который вы используете , Тем не менее, IEEE-754 64-битная двоичная с плавающей точкой является повсеместным. В этом формате числа представлены знаком, 53-битным значением и показателем степени от двух до -1022 до +1023. (Значениеи - это кодирование с использованием как 52-битного поля, так и некоторой информации из поля экспоненты, поэтому многие люди называют его 52-битным значением, но это неверно. Только основное поле для кодирования - 52 бита. фактическая значимость - 53 бита.) Этой информации о значении и с диапазоном экспоненты достаточно, чтобы понять DBL_MIN и DBL_EPSILON, поэтому я не буду обсуждать формат кодирования в этом ответе. Тем не менее, я укажу, что есть нормальные значения и субнормальные значения. Для нормальных значений: значение и значения задается двоичной цифрой «1». за ним следуют 52 бита после точки радиуса (52 бита в значимом поле). Для субнормальных значений - значение и значение, заданные «0». сопровождается 52 битами. Нормальные и субнормальные значения различаются по значению в поле экспоненты.

DBL_MIN - минимальное нормальное положительное значение. Таким образом, он имеет наименьшее нормальное значение и значение, заданное как «1,000000000000000000000000000000000000000000000000000000000000000000», что равно 1, а наименьший показатель степени - –1022. Таким образом, это + 1 • 2 -1022 , что составляет около 2,2 • 10 -308 .

DBL_EPSILON - это разница между одним и следующим представимым значением в формате с плавающей запятой. Следующее значение задается значением и двоичным «1.0000000000000000000000000000000000000000000000000001», что равно 1 + 2 −52 . Итак, DBL_EPSILON равно 2 −52 .

Что из этого следует использовать для сравнения по допускам? Ни. Чтобы получить a и b, предположительно вы выполнили некоторую операцию с плавающей точкой. В каждой из этих операций могла быть какая-то ошибка. Арифметика с плавающей точкой c приблизительно реальная арифметика c. Для каждой элементарной операции арифметика с плавающей точкой c дает вам представимое значение, которое является ближайшим к результату действительного числа. (Обычно это ближайший в любом направлении, но могут быть доступны режимы направленного округления для выбора предпочтительного направления.) Когда этот представимый результат отличается от результата действительного числа, это различие называется ошибкой округления. В режиме округления до ближайшего, ошибка округления может, как правило, составлять до 1/2 расстояния между представимыми числами в этой окрестности.

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

Понимание того, какой может быть последняя ошибка, является сложной проблемой в целом. Для этого есть целая область исследования, численный анализ . Это означает, что не может быть никаких общих рекомендаций о том, какой допуск использовать при попытке сравнить числа с плавающей запятой так, как вы хотите. Это требует изучения конкретной проблемы. Кроме того, если вы выясните, что результаты с плавающей точкой a и b могут находиться на некотором расстоянии d друг от друга, даже если результаты действительных чисел a и b будут быть равным, это не значит, что сравнивать a и b с допуском d - правильное решение. Это гарантирует, что вы не получите ложных негативов - каждый раз, когда a и b равны, ваше сравнение a и b возвращает true. Однако это позволит вам получить ложные срабатывания - иногда, когда a и b не равны, ваше сравнение a и b возвращает true.

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

0 голосов
/ 01 апреля 2020

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

DBL_MIN эффективно определяется количеством битов, используемых в показателе степени. Двойной формат IEEE 754 использует 11 бит для показателя степени, что дает ~ 2 ^ -1022 ~ = 2.2e-308 для минимального значения. Это очень мало.

DBL_EPSILON эффективно определяется количеством битов в мантиссе. IEEE 754 double использует 52 бита, давая эпсилон 2 ^ -52 ~ = 2.2e-16

Важно DBL_MIN - это значение абсолютное . DBL_EPSILON является относительным значением.

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

Ваши функции для сравнения с допуском будут зависеть от многих факторов

  • диапазон значений в вашей области (например, астрофизика не использует те же масштабы, что и микроэлектроника)
  • ваша точность требует
  • количество числовых ошибок в ваших вычислениях

Наконец, вам могут понадобиться функции сравнения с относительным допуском и абсолютным допуском или с обоими.

Вот пример (на C ++, не тестировался).

bool almostEqual(double a, double b, double relTol=1e-3, double absTolVal=1e-8)
{
    double maxVal = std::max(std::fabs(a), std::fabs(b));
    double relTolVal = maxVal*relTol;
    double diffVal = std::fabs(a - b);
    return diffVal <= relTolVal || diffVal <= absTolVal;
}

Несколько комментарии

  • Если вы не используете max или min, то будут случаи, когда almostEqual(a, b) и almostEqual(b, a) дают разные результаты.
  • Хотя вы можете шаблонизировать такая функция затрудняет выбор значений допусков по умолчанию.
  • В моем опыте (имитация аналоговой схемы) значение между 1e-2 и d 1e-4 хорош для relTol. Для absTolVal хорошим показателем является значение, которое в 1e6–1e8 раз меньше наибольшего значения в вашем домене. Например, при моделировании цепи типичные напряжения составляют около 1 В, поэтому значение 1e-6 будет подходящим абсолютным допуском напряжения. Токи имеют гораздо меньшую величину в области микроампер, поэтому абсолютный допуск по току должен быть примерно 1e-12.
  • Приведенный выше код имеет потенциальное переполнение, если a и b имеют противоположные значения. приметы. Это можно сделать, добавив знак проверки.
  • NaN может потребоваться специальная обработка .
  • Inf также требуется специальная обработка.

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

...