Перейти к основному содержанию
Если вам нужны точные вычисления, особенно при работе с финансовыми или бизнес-данными, требующими высокой точности, стоит использовать Decimal.Числа с плавающей запятой могут давать неточные результаты, как показано ниже:
CREATE TABLE IF NOT EXISTS float_vs_decimal
(
   my_float Float64,
   my_decimal Decimal64(3)
)
ENGINE=MergeTree
ORDER BY tuple();

# Сгенерировать 1 000 000 случайных чисел с 2 знаками после запятой и сохранить их как float и как decimal
INSERT INTO float_vs_decimal SELECT round(randCanonical(), 3) AS res, res FROM system.numbers LIMIT 1000000;
SELECT sum(my_float), sum(my_decimal) FROM float_vs_decimal;

┌──────sum(my_float)─┬─sum(my_decimal)─┐
499693.60500000004499693.605
└────────────────────┴─────────────────┘

SELECT sumKahan(my_float), sumKahan(my_decimal) FROM float_vs_decimal;

┌─sumKahan(my_float)─┬─sumKahan(my_decimal)─┐
499693.605499693.605
└────────────────────┴──────────────────────┘
Эквивалентные типы в ClickHouse и C приведены ниже:
  • Float32float.
  • Float64double.
Типы с плавающей запятой в ClickHouse имеют следующие псевдонимы:
  • Float32FLOAT, REAL, SINGLE.
  • Float64DOUBLE, DOUBLE PRECISION.
При создании таблиц можно задавать числовые параметры для чисел с плавающей запятой (например, FLOAT(12), FLOAT(15, 22), DOUBLE(12), DOUBLE(4, 18)), но ClickHouse их игнорирует.

Использование чисел с плавающей запятой

  • При вычислениях с числами с плавающей запятой может возникать ошибка округления.
SELECT 1 - 0.9

┌───────minus(1, 0.9)─┐
0.09999999999999998
└─────────────────────┘
  • Результат вычислений зависит от способа вычислений (типа процессора и архитектуры компьютерной системы).
  • Вычисления с числами с плавающей запятой могут давать такие значения, как бесконечность (Inf) и «не число» (NaN). Это следует учитывать при обработке результатов вычислений.
  • При разборе чисел с плавающей запятой из текста результат может не быть ближайшим числом, представимым в машинном формате.

NaN и Inf

В отличие от стандартного SQL, ClickHouse поддерживает следующие категории чисел с плавающей запятой:
  • Inf — бесконечность.
SELECT 0.5 / 0

┌─divide(0.5, 0)─┐
│            inf │
└────────────────┘
  • -Inf — Отрицательная бесконечность.
SELECT -0.5 / 0

┌─divide(-0.5, 0)─┐
-inf │
└─────────────────┘
  • NaN — не является числом.
SELECT 0 / 0

┌─divide(0, 0)─┐
│          nan │
└──────────────┘
Правила сортировки NaN см. в разделе предложение ORDER BY.

Значения NaN в семантике множеств

Стандарт IEEE 754 определяет NaN так, что скалярное сравнение NaN = NaN возвращает false. ClickHouse следует этому правилу для оператора =. Однако NaN — это не одно конкретное значение, а любой битовый шаблон, у которого экспонента состоит целиком из единиц, а мантисса не равна нулю. Разные операции и разные архитектуры CPU могут порождать значения NaN с разными знаковыми битами или разной полезной нагрузкой мантиссы. Например:
  • 0./0. даёт NaN, у которого на большинстве платформ x86 знаковый бит равен 1.
  • Литерал nan даёт NaN, у которого знаковый бит равен 0.
  • После PR #98230 путь AArch64 NEON для log возвращает NaN, у которого знаковый бит отличается от скалярного log из glibc для отрицательных входных значений.
Хеш-таблицы в ClickHouse сравнивают ключи побайтно, поэтому разные битовые шаблоны NaN хешируются в разные корзины и рассматриваются как разные значения в операциях с семантикой множеств, включая DISTINCT, GROUP BY, uniqExact, countDistinct и equi-JOIN по ключу Float:
SELECT countDistinct(arrayJoin([0./0., nan, log(-1.)]));
-- May return 2 or 3 depending on architecture and build, even though all three inputs are NaN.
Это согласуется с IEEE 754 (каждый NaN не равен ни одному другому значению, включая самого себя), но может показаться неожиданным. Если вам нужно, чтобы операции с семантикой множеств считали все значения NaN равными, приведите их к каноническому виду в запросе:
-- Replace every NaN with a single canonical NaN value
SELECT countDistinct(if(isNaN(x), CAST('nan' AS Float64), x))
FROM (SELECT arrayJoin([0./0., nan, log(-1.)]) AS x);
-- Returns 1.

-- Or exclude NaN values from the set entirely
SELECT countDistinct(if(isNaN(x), NULL, x))
FROM (SELECT arrayJoin([0./0., nan, log(-1.)]) AS x);
-- Returns 0.
Тот же подход применим к ключам DISTINCT, GROUP BY и JOIN.

BFloat16

BFloat16 — это 16-битный тип данных с плавающей запятой с 8-битными экспонентой и знаком, а также 7-битной мантиссой. Он полезен для приложений машинного обучения и ИИ. ClickHouse поддерживает преобразования между Float32 и BFloat16, которые можно выполнять с помощью функций toFloat32() или toBFloat16.
Большинство других операций не поддерживается.
Последнее изменение 25 июня 2026 г.