![]() |
![]() ![]() ![]() ![]() ![]() |
![]() |
Floating-point ComparisonBoost , Math Toolkit 2.5.0 , Chapter 2. Floating Point Utilities
|
![]() |
Note |
---|---|
Более старые компиляторы могут использовать эту формулу для вычисления |
Можно установить дисплей, включающий все нули (полезно для этого примера показать все потенциально значимые цифры), а также отображать значенияв качестве слов, а не целых чисел:
std::cout.precision(std::numeric_limits<float>::max_digits10); std::cout << std::boolalpha << std::showpoint << std::endl;
При сравнении значений, которые являютсядовольно близкимиилипримерно равными, мы могли бы использовать либоfloat_distance
, либоrelative_difference
/epsilon_difference
, например, с типомfloat
, эти два значения соседствуют друг с другом:
float a = 1; float b = 1 + std::numeric_limits<float>::epsilon(); std::cout << "a = " << a << std::endl; std::cout << "b = " << b << std::endl; std::cout << "float_distance = " << float_distance(a, b) << std::endl; std::cout << "relative_difference = " << relative_difference(a, b) << std::endl; std::cout << "epsilon_difference = " << epsilon_difference(a, b) << std::endl;
который производит продукцию:
a = 1.00000000 b = 1.00000012 float_distance = 1.00000000 relative_difference = 1.19209290e-007 epsilon_difference = 1.00000000
В приведенном выше примере так получилось, что расстояние редактирования, измеренноеfloat_distance
, и разница, измеренная в единицах эпсилона, были равны. Однако из-за способа представления значений с плавающей точкой это не всегда так:
a = 2.0f / 3.0f; // 2/3 inexactly represented as a float b = float_next(float_next(float_next(a))); // 3 floating point values above a std::cout << "a = " << a << std::endl; std::cout << "b = " << b << std::endl; std::cout << "float_distance = " << float_distance(a, b) << std::endl; std::cout << "relative_difference = " << relative_difference(a, b) << std::endl; std::cout << "epsilon_difference = " << epsilon_difference(a, b) << std::endl;
который производит продукцию:
a = 0.666666687 b = 0.666666865 float_distance = 3.00000000 relative_difference = 2.68220901e-007 epsilon_difference = 2.25000000
Существует еще одно важное различие междуfloat_distance
иrelative_difference/epsilon_difference
функциями в том, чтоfloat_distance
возвращает подписанный результат, который отражает, какой аргумент больше по величине, где какотносительная_difference/epsilon_difference
просто возвращает неподписанное значение, которое представляет, насколько далеки друг от друга значения. Например, если мы поменяем порядок аргументов:
std::cout << "float_distance = " << float_distance(b, a) << std::endl; std::cout << "relative_difference = " << relative_difference(b, a) << std::endl; std::cout << "epsilon_difference = " << epsilon_difference(b, a) << std::endl;
Результат теперь:
float_distance = -3.00000000 relative_difference = 2.68220901e-007 epsilon_difference = 2.25000000
Нули всегда рассматриваются как равные, как и бесконечности, если они имеют один и тот же знак:
a = 0; b = -0; // signed zero std::cout << "relative_difference = " << relative_difference(a, b) << std::endl; a = b = std::numeric_limits<float>::infinity(); std::cout << "relative_difference = " << relative_difference(a, b) << std::endl; std::cout << "relative_difference = " << relative_difference(a, -b) << std::endl;
который производит продукцию:
relative_difference = 0.000000000 relative_difference = 0.000000000 relative_difference = 3.40282347e+038
Обратите внимание, что конечные значения всегда бесконечно далеки от бесконечностей, даже если эти конечные значения очень велики.
a = (std::numeric_limits<float>::max)(); b = std::numeric_limits<float>::infinity(); std::cout << "a = " << a << std::endl; std::cout << "b = " << b << std::endl; std::cout << "relative_difference = " << relative_difference(a, b) << std::endl; std::cout << "epsilon_difference = " << epsilon_difference(a, b) << std::endl;
который производит продукцию:
a = 3.40282347e+038 b = 1.#INF0000 relative_difference = 3.40282347e+038 epsilon_difference = 3.40282347e+038
Наконец, все денормализованные значения и нули рассматриваются как фактически равные:
a = std::numeric_limits<float>::denorm_min(); b = a * 2; std::cout << "a = " << a << std::endl; std::cout << "b = " << b << std::endl; std::cout << "float_distance = " << float_distance(a, b) << std::endl; std::cout << "relative_difference = " << relative_difference(a, b) << std::endl; std::cout << "epsilon_difference = " << epsilon_difference(a, b) << std::endl; a = 0; std::cout << "a = " << a << std::endl; std::cout << "b = " << b << std::endl; std::cout << "float_distance = " << float_distance(a, b) << std::endl; std::cout << "relative_difference = " << relative_difference(a, b) << std::endl; std::cout << "epsilon_difference = " << epsilon_difference(a, b) << std::endl;
который производит продукцию:
a = 1.40129846e-045 b = 2.80259693e-045 float_distance = 1.00000000 relative_difference = 0.000000000 epsilon_difference = 0.000000000 a = 0.000000000 b = 2.80259693e-045 float_distance = 2.00000000 relative_difference = 0.000000000 epsilon_difference = 0.000000000
Обратите внимание, что в приведенном выше примере два денормализованных значения, которые являются фактором 2, не являются менее чем одним представлением друг от друга!
Все приведенные выше примеры содержатся вfloat_comparison_example.cpp.
Представьте, что мы тестируем следующую функцию:
double myspecial(double x) { return sin(x) - sin(4 * x); }
Эта функция имеет несколько корней, некоторые из которых вполне предикабельны в том, что обагрехах]
игреха4х
вместе равны нулю. Другие происходят потому, что значения, возвращаемые из этих двух функций, точно отменяются. В таких точках относительная разница между истинным значением функции и фактическим возвращаемым значением может бытьпроизвольно большойиз-заошибки отмены.
В таком случае тестирование функции выше, требуя, чтобы значения, возвращенныеотносительной_ошибки
илиэпсилон_ошибки
, были ниже некоторого порога, бессмысленно: лучшее, что мы можем сделать, это проверить, что абсолютная разницамежду истинными и рассчитанными значениями ниже некоторого порога.
Конечно, определить, каким должен быть этот порог, часто сложно, но хорошей отправной точкой будет машинный эпсилон, умноженный на наибольшее из суммируемых значений. В приведенном выше примере наибольшее значение, возвращаемоегрехомкаким бы то ни было]
, составляет 1, поэтому простое использование машинного эпсилона в качестве цели для максимальной абсолютной разницы может быть хорошим началом (хотя на практике нам может потребоваться немного более высокое значение - потребуются некоторые пробы и ошибки).
Статья Floating-point Comparison раздела Math Toolkit 2.5.0 Chapter 2. Floating Point Utilities может быть полезна для разработчиков на c++ и boost.
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.
:: Главная :: Chapter 2. Floating Point Utilities ::
реклама |