Всем привет! Я – Ипатов Александр, старший разработчик компании USETECH. Сегодня рассмотрим вопрос округления в Power BI, которое может привести к неочевидной разнице результатов при различной реализации в DAX.
В мире бизнес-аналитики точность расчетов имеет критическое значение. Однако даже в такой мощной системе, как Power BI, пользователи могут столкнуться с неожиданными расхождениями в цифрах. Эти различия часто обусловлены особенностями округления при работе с разными типами данных. Проблема становится особенно заметной при работе с финансовыми показателями, где каждая копейка на счету.
Округление – это не просто техническая особенность системы, а фундаментальный аспект компьютерной арифметики. В Power BI и DAX оно проявляется особенно ярко из-за:
- Разных типов данных (Currency, Decimal, Integer).
- Особенностей вычислений в столбцах и мерах.
- Различий в агрегации данных.
Хотя разницы в округлении в Power BI встречаются нечасто, но когда они возникают, пользователи обычно удивляются и разочаровываются.
Рассмотрим такой пример: расчет премий в зависимости от результатов.
Рассмотрим две реализации одной и той же бизнес–логики: мера [Премия] вычисляет 0,1% от продаж в рабочие дни и 0,2% от продаж в нерабочие. [Сумма продаж] для каждой транзакции рассчитывается как произведение ‘Продажи’[Количество] на ‘Продажи’[Стоимость].
Две меры [Премия] и [Премия v2] – реализуют одну логику, но разными способами:
Премия :=
SUMX (
‘Продажи’,
VAR Amt = ‘Продажи’[Количество] * ‘Продажи’[Стоимость]
VAR Pct = IF ( RELATED ( 'Календарь'[Рабочий день] ) = 1, 0.001, 0.002 )
RETURN
Amt * Pct
)
[Премия] применяет округление на уровне каждой транзакции.
Сумма продаж :=
SUMX ( ‘Продажи’, ‘Продажи’[Количество] * ‘Продажи’[Стоимость] )
Премия v2 :=
CALCULATE ( [Сумма продаж], 'Календарь'[Рабочий день] = 1 ) * .001
+
CALCULATE ( [Сумма продаж], 'Календарь'[Рабочий день] = 0 ) * .002
[Премия v2] сначала суммирует продажи, а затем применяет процент.
Результаты немного различаются:

[Премия] : 544 035,41
[Премия v2] : 544 035,62
Разница: 0,21 (0,00004%).
Почему так происходит? Причина кроется в типах данных
Столбец ‘Продажи’[Стоимость] имеет тип Десятичное число с фиксированной запятой (в DAX – Currency). Этот тип хранит числа с фиксированной точностью до 4 знаков после запятой (0,0001). При умножении 0,01 (минимальный шаг цены) на 0,001 (ставка бонуса) получаем 0,00001 – значение, которое не может быть точно представлено в этом формате
Умножение ‘Продажи’[Количество] (Целое число) на ‘Продажи’[Стоимость] (Currency) дает результат типа Currency.
Затем это значение умножается на 0,001 или 0,002 (тип Decimal).
По правилам DAX, умножение Currency на Decimal возвращает Currency, что приводит к округлению до 4 знаков после запятой. Поскольку ‘Продажи’[Стоимость] имеет точность 0,01, умножение на 0,001 дает 0,00001, что выходит за пределы точности Currency. В результате округление происходит на уровне каждой транзакции (в таблице более 100 тысяч записей), и накопленная погрешность становится заметной.
Какой результат правильный?
Интуитивно кажется, что [Премия v2] точнее, так как в нем меньше округлений. Однако в некоторых бизнес–сценариях требуется округлять каждую транзакцию или все транзакции клиента – тогда [Премия v2] даст неверный результат.
Другими словами: если нужно округлять каждую транзакцию – то верен [Премия], если требуется точный расчет без промежуточного округления, то [Премия v2] ближе к истине.
Как избежать различий?
1. Преобразование в Decimal перед умножением
Можно явно преобразовать промежуточный результат в Decimal, чтобы избежать округления:
Премия :=
SUMX (
‘Продажи’,
VAR Amt = ‘Продажи’[Количество] * ‘Продажи’[Стоимость]
VAR Pct = IF ( RELATED ( 'Календарь'[Рабочий день] ) = 1, 0.001, 0.002 )
RETURN
CONVERT ( Amt, DOUBLE ) * Pct
)

Тогда обе меры будут иметь один и тот же результат вычисления.
2. Использование для цен / стоимости типа «Десятичное число (с плавающей запятой)»
Если изменить тип ‘Продажи’[Стоимость] на Десятичное число, умножение сохранит плавающую точность, и результаты обеих мер, опять же, совпадут.
Практические рекомендации
Когда использовать Currency / Fixed Decimal (Десятичное число с фиксированной запятой):
- Финансовые отчеты, где важно соответствие бухгалтерским документам.
- Системы, работающие с валютами (деньги всегда имеют фиксированный формат).
- Большие объемы данных, где важна производительность.
Когда предпочесть Decimal (Десятичное число):
- Научные и инженерные расчеты.
- Cложные математические операции (особенно умножение/деление).
- Когда нужна максимальная точность без промежуточного округления.
Абсолютная точность vs практическая необходимость:
В реальном бизнесе часто приходится балансировать между:
- Математической точностью
- Требованиями нормативных документов
- Производительностью системы
- Ожиданиями пользователей
Интересный парадокс: иногда "неправильное" округление оказывается более правильным с точки зрения бизнеса, если оно соответствует принятым в организации стандартам.
Выводы
Разницы в округлении обычно незначительны, но если требуется точность до копейки, важно:
1. Округление может влиять на итоговые значения, особенно при большом количестве операций.
2. Типы данных в DAX критичны – Currency округляет промежуточные результаты, Decimal сохраняет точность.
3. Выбор подхода зависит от требований:
3.1. Если нужна точность до копеек на уровне транзакций – используйте Currency.
3.2. Если важна математическая точность – переходите на Decimal.
4. Проверяйте логику расчетов, особенно если сравниваете разные реализации одной формулы.
В большинстве случаев разница незначительна, но в финансовых моделях даже 0,001% может иметь значение. Всегда учитывайте возможные побочные эффекты округления при работе с DAX!
Комментарии