При работе с числовыми данными в запросах платформы 1С:Предприятие разработчики часто сталкиваются с ситуацией, когда результатом деления или вычисления средних величин становится число с избыточной точностью. Например, вместо ожидаемых двух или трех знаков после запятой СУБД возвращает длинную дробь вида 0.948545632894753533. Это не только перегружает интерфейс отчета, но и усложняет восприятие информации пользователями.
В этой статье мы подробно разберем, как укоротить дробное число непосредственно на этапе написания запроса, выясним причины автоматического усечения разрядов при помещении во временные таблицы, а также сопоставим этот метод с визуальным форматированием в Системе компоновки данных (СКД).
Для явного приведения типов и ограничения разрядности чисел в языке запросов 1С предусмотрена стандартная конструкция ВЫРАЗИТЬ. Она позволяет округлить исходное дробное число до нужного количества знаков после запятой по общепринятым математическим правилам.
Давайте разберем типичный пример. Предположим, у нас есть выражение, рассчитывающее соотношение времени работы оборудования к времени его простоя. Исходный вариант запроса с условием может выглядеть следующим образом:
ВЫБОР
КОГДА ЕСТЬNULL(ПростойОборудования.ИтогоПростой, 0) > 0
ТОГДА ОборудованиеСправочник.КоличествоЧасовРаботы / ПростойОборудования.ИтогоПростой
ИНАЧЕ 0
КОНЕЦ
Чтобы ограничить точность результата деления тремя знаками после запятой, нам необходимо обернуть саму математическую операцию деления в функцию ВЫРАЗИТЬ. Посмотрим на правильную реализацию этой конструкции:
ВЫБОР
КОГДА ЕСТЬNULL(ПростойОборудования.ИтогоПростой, 0) > 0
ТОГДА ВЫРАЗИТЬ(ОборудованиеСправочник.КоличествоЧасовРаботы / ПростойОборудования.ИтогоПростой КАК ЧИСЛО(15, 3))
ИНАЧЕ 0
КОНЕЦ
Обратите внимание на важную деталь: функция ВЫРАЗИТЬ применяется именно к результату математической операции (деления), а не ко всей конструкции ВЫБОР ... КОНЕЦ. Попытка поместить все условие внутрь функции приведения типов приведет к синтаксической ошибке в конструкторе запросов.
При использовании конструкции ВЫРАЗИТЬ(... КАК ЧИСЛО(Длина, Точность)) необходимо строго соблюдать правила определения разрядности, иначе запрос завершится аварийно во время выполнения.
Длина (в нашем примере — 15) — определяет общее количество знаков в числе, включая как целую часть до запятой, так и дробную часть после нее.Точность (в нашем примере — 3) — определяет жесткое количество знаков, которое будет оставлено после запятой.Длина - Точность. Например, для типа ЧИСЛО(5, 2) максимальное число, которое система сможет обработать, составляет всего 999.99. Если в результате расчетов получится 1000.00, произойдет сбой системы.Именно поэтому мы рекомендуем всегда закладывать общую длину числа с запасом. Использование параметров вроде ЧИСЛО(15, 2) или ЧИСЛО(15, 3) является наиболее безопасным стандартом разработки в 1С.
Давайте проанализируем поведение платформы при работе с временными таблицами. Многие разработчики замечают, что при записи результатов расчета во временную таблицу с помощью инструкции ПОМЕСТИТЬ, СУБД самостоятельно обрезает дробные числа до 8 знаков после запятой.
Выясним причину этого явления. Когда платформа 1С транслирует запрос к СУБД для создания временной таблицы, она должна явно определить типы полей создаваемой таблицы. Если колонка является результатом математического деления двух чисел и для нее не задан тип вручную, СУБД по умолчанию выделяет под нее числовой тип с фиксированной точностью в 8 знаков после запятой.
Если для бизнес-логики вашего приложения критически важна более высокая точность расчетов внутри временных таблиц (например, при сложных финансовых или технологических калькуляциях), вам необходимо явно указывать желаемую точность с помощью функции ВЫРАЗИТЬ еще на этапе первого запроса пакета, до помещения данных во временную таблицу.
Оператор ВЫРАЗИТЬ всегда выполняет стандартное математическое округление (значения от 0.5 округляются в большую сторону). Однако в реальной практике нередко возникают задачи, требующие усечения («отрезания») лишних знаков без математического округления вверх либо, наоборот, всегда округления в большую сторону.
Поскольку в языке запросов 1С отсутствуют специализированные математические функции вроде ОКР(), ЦЕЛОЕ() или ВВЕРХ(), мы можем решить эти задачи с помощью несложных арифметических алгоритмов.
Чтобы отсечь знаки после запятой без округления в большую сторону, мы можем искусственно сместить число в меньшую сторону перед применением функции ВЫРАЗИТЬ. Для этого из исходного значения вычитается половина единицы младшего сохраняемого разряда.
ВЫРАЗИТЬ(Таблица.ЧисловоеПоле - 0.5 КАК ЧИСЛО(15, 0))
ВЫРАЗИТЬ(Таблица.ЧисловоеПоле - 0.005 КАК ЧИСЛО(15, 2))
ВЫРАЗИТЬ(Таблица.ЧисловоеПоле - 0.0005 КАК ЧИСЛО(15, 3))
Важное замечание: Данный метод корректно работает для положительных чисел. Если ваше поле может содержать отрицательные значения, логику смещения необходимо делать зеркальной, используя конструкцию ВЫБОР КОГДА.
Если перед вами стоит задача всегда округлять результат в большую сторону (например, при расчете необходимого количества упаковок товара, где любая дробная часть должна трактоваться как целая единица в плюс), применяется обратный сдвиг:
ВЫРАЗИТЬ(Таблица.КоличествоУпаковок + 0.4999999999 КАК ЧИСЛО(15, 0))
Вторым мощным инструментом борьбы с избыточной точностью дробных чисел является использование механизмов Системы компоновки данных. Если округленное число требуется только для вывода на экран пользователю и не участвует в последующих расчетах, правильнее наложить маску отображения.
Давайте пошагово рассмотрим, как настроить округление в интерфейсе СКД:
ЧДЦ=3.Этот способ позволяет сохранить исходную максимальную точность числа в оперативной памяти компьютера для корректных математических итогов, но при этом покажет пользователю аккуратные и понятные данные.
Выбор места округления чисел — это не просто дело вкуса программиста, а серьезное архитектурное решение. Давайте детально проанализируем, в каких ситуациях следует применять каждый из подходов.
Рассмотрим классическую проблему расхождения копеек при суммировании округленных данных на практическом примере.
Предположим, в нашей базе данных хранятся две строки со значениями 1.4 и 1.4.
Если мы применим функцию ВЫРАЗИТЬ(Число КАК ЧИСЛО(15, 0)) в тексте запроса, система вернет нам жестко округленные целые числа:
11При расчете итогов (суммы) отчет послушно сложит эти значения и вернет результат: 2. Математическая логика отчета с точки зрения пользователя будет идеальной: 1 + 1 = 2. Однако реальная сумма данных в базе (2.8) в этот отчет не попадет, мы безвозвратно потеряли точность.
Если мы оставим числа в запросе нетронутыми, а округление до целого настроим через формат вывода в СКД, расчеты будут происходить по следующему алгоритму:
1.4 и 1.4.1 и 1.1.4 + 1.4 = 2.8.В результате на экране появится таблица, в которой строки равны 1 и 1, а итоговая сумма равна 3. Это вызовет массу вопросов у пользователей и аудиторов, проверяющих отчеты.
Подведем краткий итог по выбору архитектуры округления:
ВЫБОР), либо когда строго требуется схождение арифметических сумм по строкам отчета.Завершая разбор работы с дробными числами, нельзя обойти стороной проблему обработки пустых значений. Если в вашем запросе происходит деление полей, которые физически могут содержать значение NULL (например, при использовании левых соединений таблиц), применение функции ВЫРАЗИТЬ к результату деления без предварительной проверки приведет к аварийному завершению работы программы.
Перед округлением всегда используйте функцию ЕСТЬNULL для замены неопределенных значений на числовой ноль или единицу. Давайте посмотрим на эталонный пример безопасного округления:
ВЫРАЗИТЬ(ЕСТЬNULL(Таблица.СуммаДокумента, 0) / ЕСТЬNULL(Таблица.КурсВалюты, 1) КАК ЧИСЛО(15, 2))
В данном примере мы гарантируем, что даже при отсутствии курса валюты деление произойдет на единицу (что исключит ошибку деления на ноль), а отсутствие суммы будет обработано как ноль, после чего результат безопасно округлится до двух знаков после запятой.