К сожалению, нет «хороших» правил для выбора допуска.
В вашем распоряжении «машинный эпсилон»
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. Это позволяет вам не кодировать логику сравнения в ваших алгоритмах.
Короче говоря, не существует единого правила для всех. Вы должны выбрать в зависимости от проблемы