двойная гранулярность влияет на числовые сравнения c ++ - PullRequest
1 голос
/ 26 января 2012

Ниже приведен тестовый блок кода, который должен определить эквивалентность 2 двойных значений в пределах допуска.

double lhs_1 = 0.02;
double lhs_2 = 0.04;
double rhs = 0.03;
double tolerance = 0.01;

bool is_match_1 = (abs(lhs_1 - rhs) <= tolerance);
bool is_match_2 = (abs(lhs_2 - rhs) <= tolerance);

Однако is_match_2 оказывается ложным, когда is_match_1 оказывается истинным.Я понимаю, что числа, хранящиеся в компьютере, являются дискретными значениями, а не непрерывными.Может кто-нибудь поделиться решением?Я хотел бы ошибиться в сторону прохождения теста в разумных пределах.Есть ли способ увеличить значение double на 1 для той точности, которая у него есть в настоящее время (я не знаком с битовой компоновкой double)?Потому что я мог бы просто увеличить значение допуска, чтобы учесть эту проблему гранулярности.

РЕДАКТИРОВАТЬ:

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

Ответы [ 4 ]

2 голосов
/ 26 января 2012

К сожалению, нет «хороших» правил для выбора допуска.

В вашем распоряжении «машинный эпсилон»

double epsilon = std::numeric_limits<double>::epsilon()

это наименьшее значение, которое, добавленное к одному, дает результат, отличный от единицы.

Я обычно пишу свои допуски как функцию эпсилона. Хороших правил не существует, но, например, сравнивать вот так

bool fuzzy_equals(double a, double b)
{
    static const double eps = std::numeric_limits<double>::epsilon();
    return std::fabs(b - a) < 1024 * eps * std::max(a, b);
}

хорошо работает во многих случаях. Вы можете настроить 1024, мне нравятся полномочия двух, но вы не можете. Фактическое значение, которое вы выбираете, зависит от проблемы. Эпсилон для двойников составляет около 10 ^ -16, поэтому 1024 довольно мало, и вам понадобится большее число во многих случаях (практически любая операция, включая минус внутри fuzzy_equals, "съест" один эпсилон - они могут отменить, но в среднем n операции означают sqrt(n) * epsilon точность, поэтому 1024 соответствует ожидаемой точности после миллиона операций) .

В других случаях, когда точность не так хороша, например, при проверке минимума функции по известному значению (минимумы обычно определяются только с точностью до sqrt (eps)), я использую

bool fuzzy_equals2(double a, double b)
{
    static const double eps = std::numeric_limits<double>::epsilon();
    return std::fabs(b - a) < 1024 * std::sqrt(eps) * std::max(a, b);
}

Я часто использую другие функции, например std::pow(eps, something) или даже -1 / std::log(eps). Это зависит от того, какую предварительную информацию я могу извлечь из проблемы, и какую ошибку я ожидаю .

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

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

2 голосов
/ 26 января 2012
0 голосов
/ 26 января 2012

Ваш допуск уже очень слабый - 0,01 ОГРОМНО по сравнению с числами, которые вы сравниваете.Просто откройте его до 0,01000001, и все будет в порядке.

0 голосов
/ 26 января 2012

Весь смысл наличия допуска - избегать простых проверок на равенство. Они на самом деле не работают на двоих, так как вы только что узнали трудный путь. В мире двойников 1 + 1 может не равняться 2 (как внутренне это может быть что-то вроде 1.99999743).

Так что «Разница равна допуск» не является надежным условием. Допуск должен быть на 1-2 порядка меньше, чем разумная разница между значениями, в отличие от , такой же, как ожидаемая разница . Поэтому, если вы хотите проверить, соответствует ли lhs_2 - rhs значению rhs - lhs_1 в пределах допуска, следующая проверка поможет вам лучше:

fabs((lhs_2 - rhs) - (rhs - lhs_1)) < 0.0001
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...