Перейти к содержимому
Последнее обновление

Типы с плавающей точкой (FLOAT и DOUBLE)

Описание​

Doris предоставляет два типа данных с плавающей точкой: FLOAT и DOUBLE. Это числовые типы с переменной точностью, соответствующие стандарту IEEE 754 для арифметики с плавающей точкой.

ТипПсевдонимРазмер храненияОписание
FLOATFLOAT4, REAL4 байтаЧисло с плавающей точкой одинарной точности
DOUBLEFLOAT8, DOUBLE PRECISION8 байтЧисло с плавающей точкой двойной точности

Диапазон значений​

FLOAT​

Doris использует числа с плавающей точкой одинарной точности стандарта IEEE-754 со следующим диапазоном значений:

  • -∞ (-Infinity)
  • [-3.402E+38, -1.175E-37]
  • 0
  • [1.175E-37, 3.402E+38]
  • +∞ (+Infinity)
  • NaN (не число)

Подробнее см. Тип float в C++ и Формат чисел с плавающей точкой одинарной точности в Википедии.

DOUBLE​

Doris использует числа с плавающей точкой двойной точности стандарта IEEE-754 со следующим диапазоном значений:

  • -∞ (-Infinity)
  • [-1.79769E+308, -2.225E-307]
  • 0
  • [+2.225E-307, +1.79769E+308]
  • +∞ (+Infinity)
  • NaN (не число)

Подробнее см. Тип double в C++ и Формат чисел с плавающей точкой двойной точности в Википедии.

Специальные значения​

Помимо обычных числовых значений, типы с плавающей точкой поддерживают несколько специальных значений, соответствующих стандарту IEEE 754:

  • Infinity или Inf: положительная бесконечность
  • -Infinity или -Inf: отрицательная бесконечность
  • NaN: не число

Эти специальные значения можно получить с помощью преобразований CAST:

mysql> select cast('NaN' as double), cast('inf' as double), cast('-Infinity' as double);
+-----------------------+-----------------------+-----------------------------+
| cast('NaN' as double) | cast('inf' as double) | cast('-Infinity' as double) |
+-----------------------+-----------------------+-----------------------------+
| NaN | Infinity | -Infinity |
+-----------------------+-----------------------+-----------------------------+

У чисел с плавающей точкой есть и неочевидное свойство: существуют два разных нулевых значения, +0 и -0. Хотя в большинстве случаев они считаются равными, их знаковые биты различаются:

mysql> select cast('+0.0' as double), cast('-0.0' as double);
+------------------------+------------------------+
| cast('+0.0' as double) | cast('-0.0' as double) |
+------------------------+------------------------+
| 0 | -0 |
+------------------------+------------------------+

Операции с числами с плавающей точкой​

Арифметические операции​

Числа с плавающей точкой в Doris поддерживают обычные арифметические операции: сложение, вычитание, умножение и деление.

Следует учитывать, что при делении чисел с плавающей точкой на 0 Doris не полностью следует стандарту IEEE 754.

В этом отношении Doris следует реализации PostgreSQL и при делении на 0 возвращает SQL NULL вместо специальных значений:

ВыражениеPostgreSQLIEEE 754Doris
1.0 / 0.0ОшибкаInfinityNULL
0.0 / 0.0ОшибкаNaNNULL
-1.0 / 0.0Ошибка-InfinityNULL
'Infinity' / 'Infinity'NaNNaNNaN
1.0 / 'Infinity'0.00.00
'Infinity' - 'Infinity'NaNNaNNaN
'Infinity' - 1.0InfinityInfinityInfinity

Операции сравнения​

Стандарт IEEE определяет правила сравнения чисел с плавающей точкой, которые существенно отличаются от обычных правил сравнения целых чисел. Например, отрицательный и положительный ноль считаются равными, а любое значение NaN не равно никакому другому значению, включая само себя. Все конечные числа с плавающей точкой строго меньше +∞ и строго больше -∞.

Чтобы обеспечить согласованность и предсказуемость результатов, Doris обрабатывает NaN иначе, чем предписывает стандарт IEEE. В Doris значение NaN считается больше всех остальных значений, включая Infinity, а NaN равно NaN.

mysql> select * from sort_float order by d;
+------+-----------+
| id | d |
+------+-----------+
| 5 | -Infinity |
| 2 | -123 |
| 1 | 123 |
| 4 | Infinity |
| 8 | NaN |
| 9 | NaN |
+------+-----------+

mysql> select
cast('Nan' as double) = cast('Nan' as double) ,
cast('Nan' as double) > cast('Inf' as double) ,
cast('Nan' as double) > cast('123456.789' as double);
+-----------------------------------------------+-----------------------------------------------+------------------------------------------------------+
| cast('Nan' as double) = cast('Nan' as double) | cast('Nan' as double) > cast('Inf' as double) | cast('Nan' as double) > cast('123456.789' as double) |
+-----------------------------------------------+-----------------------------------------------+------------------------------------------------------+
| 1 | 1 | 1 |
+-----------------------------------------------+-----------------------------------------------+------------------------------------------------------+

Проблемы точности чисел с плавающей точкой​

Приближённые значения и потеря точности​

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

Например:

mysql> SELECT CAST(1.3 AS FLOAT) - CAST(0.7 AS FLOAT) = CAST(0.6 AS FLOAT);
+--------------------------------------------------------------+
| CAST(1.3 AS FLOAT) - CAST(0.7 AS FLOAT) = CAST(0.6 AS FLOAT) |
+--------------------------------------------------------------+
| 0 |
+--------------------------------------------------------------+

Из-за погрешностей представления чисел с плавающей точкой результат этого выражения может отличаться от ожидаемого TRUE.

Операции не подчиняются закону ассоциативности​

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

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

Агрегатные функции​

При применении агрегатных функций к значениям с плавающей точкой могут накапливаться погрешности, особенно при работе с большими наборами данных. Если данные содержат чрезвычайно большие или малые значения, эти погрешности могут усиливаться. Из-за неопределённого порядка вычислений многократное выполнение одной и той же агрегатной функции может давать разные результаты, если данные содержат экстремальные значения.

Операции соединения​

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

Вывод чисел с плавающей точкой​

Начиная с 4.1.4, при преобразовании числа с плавающей точкой в строку Doris выводит самую короткую строку, позволяющую восстановить исходное значение без потерь: при обратном преобразовании этой строки в FLOAT / DOUBLE получается в точности то же двоичное значение, а лишние цифры не выводятся.

Правила форматирования вывода:

  • Запись с фиксированной точкой используется, когда десятичный порядок попадает в диапазон [-4, 16).
  • В остальных случаях используется научная запись со строчной буквой e, явно указанным знаком и не менее чем двумя цифрами в показателе степени, например 1.23456e-05.
  • Положительный и отрицательный ноль выводятся по-разному (0 и -0).
  • Написание NaN, Infinity и -Infinity остаётся прежним.
mysql> select cast('0.000123456' as double), cast('0.0000123456' as double);
+-------------------------------+--------------------------------+
| cast('0.000123456' as double) | cast('0.0000123456' as double) |
+-------------------------------+--------------------------------+
| 0.000123456 | 1.23456e-05 |
+-------------------------------+--------------------------------+
Изменение поведения (4.1.4)

В версиях до 4.1.4 выводилось фиксированное количество значащих цифр (около 7 для FLOAT и около 16 для DOUBLE), поэтому обратное преобразование выведенной строки не всегда восстанавливало исходное значение. После перехода на кратчайшее представление с восстановлением исходного значения сохранённые значения не меняются; измениться может только их текстовое представление. Например:

До 4.1.4Начиная с 4.1.4
246.9120025634766246.91200256347656
15241.3842524765615241.384252476564
11.1110756131770711.111075613177073
Значение DOUBLE 2.0 в некоторых видах текстового вывода отображалось как 2.0отображается как 2
0.00001234561.23456e-05

Изменение затрагивает множество способов вывода: результаты запросов по протоколу MySQL, CAST(... AS STRING), отображение элементов ARRAY / MAP / STRUCT, отображение JSON / VARIANT, текстовое представление в CSV / Parquet / ORC при выполнении SELECT INTO OUTFILE, EXPORT, значения, возвращаемые Stream Load, и столбцы с плавающей точкой во внешних таблицах (Hive / Iceberg / Paimon / JDBC / ES / TVF).

Если система, получающая эти данные, проверяет точное совпадение строковых представлений чисел с плавающей точкой (например, при сравнении экспортированных файлов), после обновления внесите в неё соответствующие изменения.

Рекомендации​

  1. Выбирайте подходящий тип данных: для финансовых расчётов и других сценариев, требующих точных числовых значений, используйте тип DECIMAL вместо типов с плавающей точкой.

  2. С осторожностью сравнивайте значения на равенство: избегайте прямого сравнения двух значений с плавающей точкой на равенство, особенно в операциях JOIN.

  3. Будьте осторожны при преобразованиях в строки: преобразование чисел с плавающей точкой в строки и обратно может приводить к дополнительной потере точности.

  4. Учитывайте различия между платформами: разные системы баз данных могут немного по-разному обрабатывать операции с плавающей точкой, особенно в особых случаях, связанных с NaN и Infinity (хотя большинство систем баз данных в целом следует стандарту IEEE).

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

Ключевые слова​

FLOAT, FLOAT4, REAL, DOUBLE, DOUBLE PRECISION, FLOAT8, плавающая точка