Files
ubbook/numeric/floats.md
T
2020-12-17 22:41:53 +03:00

3.1 KiB
Raw Blame History

Числа с плавающей точкой

С float и double в принципе всегда все сложно. Особенно в C++.

Стандарт C++ не требует следования стандарту IEEE 754, потому деление на ноль в вещественных числах также считается неопределенным поведением, несмотря на то, что по IEEE 754 выражение x/0.0 определяется как -INF, NaN, или INF в зависимости от знака числа x (NaN для нуля).

Сравнение вещественных чисел -- излюбленная головная боль. Вещественные числа до C++20 нельзя было использовать в качестве параметров-значений в шаблонах. Теперь же можно. Правда, ожидать, что вы насчитаете в run-time и в compile-time одно и то же -- не стоит.

Выражение x == y фактически является кривым побитовым сравнением для чисел с плавающей точкой, по особенному работающее со случаями -0.0 и +0.0, и NaN. О существовании этого и != операторов для вещественных чисел стоит забыть и никогда не вспонимать.

Для побитового сравнения нужно использовать memcmp. Для сравнения чисел -- приближенные варианты вида std::abs(x - y) < EPS, где EPS -- какое-то абсолютное или вычисляемое на основе x и y значение. А также различные манипуляции с ULP сравниваемых чисел.

Так как стандарт C++ не форсирует IEEE 754, проверки на x == NaN через его свойство (x != x) == true могут быть убраны компилятором, как заведомо ложные. Проверять нужно с помощью предназначенных для этого функций std::isnan.

Поддерживается или нет IEEE 754 можно проверить с помощью предопределенной константы std::numeric_limits<FloatType>::is_iec559

Сужающие преобразования из float в знаковые или беззнаковые целые могут повлечь неопределенное поведение, если значение непредставимо в целочисленном типе. Никаких обрезок по модулю 2^N не предполагается.

constexpr uint16_t x = 1234567.0; // CE, undefined behavior

Полезные ссылки

  1. https://en.cppreference.com/w/cpp/numeric/math/isnan
  2. https://bitbashing.io/comparing-floats.html