Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Floating-point Comparison

Boost , Math Toolkit 2.5.0 , Chapter 2. Floating Point Utilities

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Сравнение значений с плавающей точкой всегда было источником бесконечных трудностей и путаницы.

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

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

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

Некоторое фоновое чтение:

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

  • Абсолютная разница/ошибка: абсолютная разница между двумя значениямиaиbпростоfabsa-b. Это единственное значимое сравнение, если мы знаем, что результат может иметь ошибку отмены.
  • Расстояние редактирования между двумя значениями: т.е. сколько (двоичных) значений с плавающей точкой между двумя значениямиaиb? Это обеспечивается функциейBoost.Math float_distance, но, вероятно, полезно только тогда, когда вы знаете, что расстояние должно быть очень маленьким. Эту функцию довольно сложно вычислить, и она не масштабируется до значений, которые очень далеки друг от друга. Другими словами, использовать с осторожностью.
  • Относительное расстояние/ошибка между двумя значениями. Это быстро и легко вычислить, и, как правило, это метод выбора при проверке того, что ваши результаты «допустимо близки» друг к другу. Тем не менее, это не так точно, как расстояние редактирования при работе с небольшими различиями, и из-за способа кодирования значений с плавающей запятой может «колебаться» в 2 раза по сравнению с «истинным» расстоянием редактирования. Это метод, описанный ниже: еслиfloat_distanceявляется скальпелем хирурга, тоотносительная_differenceбольше похожа на швейцарский армейский нож: оба имеют важные, но разные варианты использования.
Relative Comparison of Floating-point Values

#include<boost/math/special_functions/relative_difference.hpp>

template <class T, class U>
calculated-result-type relative_difference(T a, U b);
template <class T, class U>
calculated-result-type epsilon_difference(T a, U b);

Функцияотносительное различиевозвращает относительное расстояние/ошибкуEмежду двумя значениями, определенными:

E = fabs((a - b) / min(a,b))

Функцияepsilon_differenceявляется функцией удобства, которая возвращаетотносительную_differencea,b/eps, гдеepsявляется машинным epsilon для типа результата.

Следующие специальные дела рассматриваются следующим образом:

  • Если один изaилиbявляется NaN, то возвращает наибольшее репрезентабельное значение для T: например, для типадвойной, этоstd::числовой_предел<двойной, который является таким же, какDBL_MAXили1.79769313483157e + 308.
  • Еслиaиbразличаются по знаку, то возвращается наибольшее представляемое значение для T.
  • Если обаaиbявляются бесконечностями (одного и того же знака), то возвращается ноль.
  • Если только одно изaиbявляется бесконечностью, то возвращается наибольшее представляемое значение для T.
  • Еслиaиbравны нулю, то возвращается ноль.
  • Если только одно изaилиbявляется нулем или денормализованным значением, то оно рассматривается как наименьшее (не денормализованное) значение, представляемое в T для целей вышеприведенного расчета.

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

Examples

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

using namespace boost::math;

или

using boost::math::relative_difference;
using boost::math::epsilon_difference;
using boost::math::float_next;
using boost::math::float_prior;

Следующие примеры отображают значения со всеми, возможно, значительными цифрами. Более новые компиляторы должны обеспечиватьstd::numeric_limitsFPT>::max_digits10для этой цели, и здесь мы используемfloatточность, гдеmax_digits10= 9, чтобы избежать отображения отвлекающего числа десятичных цифр.

[Note] Note

Более старые компиляторы могут использовать эту формулу для вычисленияmax_digits10std::numeric_limits<FPT>::цифры10:
& #8192;intmax_digits10=+std<FPT>::цифры10301010000 [208

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

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.

Handling Absolute Errors

Представьте, что мы тестируем следующую функцию:

double myspecial(double x)
{
   return sin(x) - sin(4 * x);
}

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

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

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


PrevUpHomeNext

Статья Floating-point Comparison раздела Math Toolkit 2.5.0 Chapter 2. Floating Point Utilities может быть полезна для разработчиков на c++ и boost.




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



:: Главная :: Chapter 2. Floating Point Utilities ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 18:41:11/0.0095310211181641/0