Каждый разработчик на платформе «1С:Предприятие» рано или поздно сталкивается с ситуацией, когда числовое поле в печатной форме или отчете упорно отказывается выводить незначащие нули в дробной части. Казалось бы, мы явно указали в запросе конструкцию ВЫРАЗИТЬ(Поле КАК ЧИСЛО(15, 2)), но на печати вместо красивого «125.00» или «12.50» все равно получаем «125» и «12.5». Почему так происходит, где кроется ошибка и как правильно решать эту задачу? Давайте разберем этот механизм детально и по шагам.
Для того чтобы понять причину такого поведения платформы, нам необходимо четко разграничить два важных понятия: физический тип данных в СУБД и визуальное (строковое) представление значения в интерфейсе пользователя.
Когда мы пишем в запросе ВЫРАЗИТЬ(Таблица.Цена КАК ЧИСЛО(15, 2)), эта инструкция транслируется на уровень СУБД (например, MS SQL Server, PostgreSQL или IBM DB2) в конструкцию явного приведения типов CAST(... AS NUMERIC(15, 2)). На уровне базы данных поле действительно получает жесткий тип с точностью ровно два знака после запятой. СУБД возвращает платформе 1С числовое значение именно в таком формате.
Однако, как только эти данные попадают во встроенный язык 1С, они преобразуются в стандартный примитивный тип Число. Во встроенном языке 1С переменные типа Число хранятся как вещественные числа высокой точности. В самой переменной нет и не может быть метаданных о том, сколько именно знаков после запятой «желает» видеть конечный пользователь на экране или в печатном документе.
Когда платформа пытается вывести это число на экран, в текстовое поле или в ячейку макета печатной формы, происходит неявное преобразование числа в строку. По умолчанию стандартный алгоритм преобразования типов в 1С отсекает все незначащие нули в дробной части. Платформа делает это из соображений лаконичности интерфейса: число «15.00» превращается в строку «15», а «12.50» превращается в «12,5». Физическое значение в памяти при этом не меняется, меняется лишь его текстовая репрезентация для пользователя.
Проанализируем интересную дискуссию, возникшую среди участников обсуждения. Некоторые разработчики утверждают, что они всегда использовали ВЫРАЗИТЬ и у них все выводилось корректно без дополнительных настроек. В чем же секрет такого расхождения? Все зависит от того, куда именно и каким образом выводится результат выполненного запроса:
ВЫРАЗИТЬ(... КАК ЧИСЛО(15, 2)), СКД автоматически считывает информацию о типе и применяет к нему стандартное форматирование по умолчанию, выводя ровно два знака после запятой, включая концевые нули.Число(15, 2), то элемент управления на форме (например, поле ввода или поле ячейки таблицы), связанный с этим реквизитом, автоматически применит маску форматирования под этот тип данных, отображая копейки.Область.Параметры.ПечЦена = Выборка.Цена), метаданные типа полностью теряются. Платформа видит обычное примитивное число и преобразует его по общим правилам операционной системы, убирая «лишние» нули.Это самый простой, чистый и рекомендуемый способ для большинства печатных форм. Нам не нужно усложнять запрос или писать лишний код во встроенном языке. Настройку формата можно выполнить визуально в Конфигураторе. Для изменения макетов напрямую в пользовательском режиме подойдёт инструкция по редактированию печатных форм в программах 1С.
Рассмотрим пошагово, как настроить формат ячейки макета:
ПечЦена).F4 или через контекстное меню).2. Также можно установить флажок «Выводить нулевые значения» или настроить группировку триад цифр (чтобы большие числа разделялись пробелами, например, «1 500.00»).ЧДЦ=2.После этой настройки, даже если в параметре будет передано простое целое число 12, табличный документ принудительно отобразит его в печатной форме как 12,00.
Если мы формируем текстовую строку программно, выводим данные в лог, отправляем email или формируем XML/JSON-файлы вручную, нам поможет встроенная функция платформы Формат(). Она принимает два параметра: само преобразуемое значение и специальную строку формата.
Посмотрим на пример кода, демонстрирующий этот подход:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ПоступлениеРасходы.НомерСтроки КАК ПечНомПП,
| ПоступлениеРасходы.Цена КАК Цена
|ИЗ
| Документ.ПоступлениеУслугПрочихАктивов.Расходы КАК ПоступлениеРасходы";
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
// Неправильно (нули будут отброшены при неявном приведении в строку):
СтрокаВыводаПростая = "Позиция: " + Выборка.ПечНомПП + ", Цена: " + Выборка.Цена;
// Правильно (всегда будет выведено ровно 2 знака после запятой):
СтрокаВыводаФорматная = "Позиция: " + Выборка.ПечНомПП + ", Цена: " + Формат(Выборка.Цена, "ЧДЦ=2");
Сообщить(СтрокаВыводаФорматная);
КонецЦикла;
Параметр ЧДЦ=2 в строке формата расшифровывается как «Число Дробная Часть = 2». Платформа гарантирует, что в результирующей строке всегда будет присутствовать ровно два десятичных знака, независимо от того, целое это число или дробное.
Если мы разрабатываем отчет на базе СКД, лучшей практикой будет задать формат вывода непосредственно в настройках схемы компоновки. Это позволит сохранить гибкость отчета, корректное поведение группировок и правильную типизацию данных для последующего экспорта отчета пользователем в Excel.
Выполним следующие шаги в конструкторе схемы компоновки данных:
Цена или Сумма).ЧДЦ=2).Теперь при выводе отчета СКД сама позаботится о форматировании чисел, и в колонках всегда будут отображаться копейки, даже если их значения равны нулю.
Несмотря на то, что оператор ВЫРАЗИТЬ напрямую не решает задачу визуального форматирования, он критически важен для предварительной математической подготовки данных в запросах. Разберем несколько неочевидных тонкостей его работы, о которых должен знать каждый разработчик.
Во-первых, помните, что ВЫРАЗИТЬ выполняет полноценное математическое округление по правилам округления к ближайшему целому (в зависимости от СУБД, это может быть стандартное математическое округление, где 0.5 округляется вверх, или банковское округление к ближайшему четному числу). Он не просто «отрезает» лишние знаки, а округляет значение.
Во-вторых, если вам требуется округлить число строго вниз (выполнить усечение дробной части без математического округления в большую сторону), в языке запросов 1С нет готовой функции вроде ЦЕЛ(). В таких случаях программисты используют проверенный математический прием: вычитание половины шага округления перед вызовом функции ВЫРАЗИТЬ.
Посмотрим на примеры таких конструкций в запросе:
ВЫБРАТЬ
// Округление до целого числа вниз (усечение дробной части):
ВЫРАЗИТЬ(Таблица.Сумма - 0.5 КАК ЧИСЛО(15, 0)) КАК СуммаБезКопеекВниз,
// Округление до сотых (до копеек) строго в меньшую сторону:
ВЫРАЗИТЬ(Таблица.Сумма - 0.005 КАК ЧИСЛО(15, 2)) КАК СуммаСКопейкамиВниз
ИЗ
Справочник.Номенклатура КАК Таблица
В-третьих, не забывайте о накоплении погрешности вычислений в СУБД при работе с дробными числами. При делении больших чисел СУБД может рассчитывать промежуточные данные с очень высокой точностью (до 8 и более знаков после запятой). В некоторых версиях платформы из-за особенностей вещественной арифметики число 125 в результате деления и последующего умножения могло превратиться в 124.99999999. При последующем округлении через ВЫРАЗИТЬ(... КАК ЧИСЛО(15, 2)) мы неожиданно получали 124.99 вместо ожидаемых 125.00.
Чтобы гарантировать точность расчетов и избежать неявных ошибок СУБД, фирма «1С» официально рекомендует применять оператор ВЫРАЗИТЬ ко всем промежуточным операндам (множителям и делителям) в сложных формулах внутри запроса, а не только к финальному результату. Для проверки и отладки сложных выражений в запросах можно использовать инженерную консоль запросов для 1С.
При неявном преобразовании чисел в строку платформа 1С ориентируется на региональные настройки операционной системы (или настройки сеанса 1С). Например, в качестве разделителя дробной части может использоваться запятая (стандарт для России) или точка (стандарт для США).
Если вы хотите, чтобы числа выводились в строго определенном формате независимо от настроек компьютера конкретного пользователя, вам необходимо явно задавать параметры локализации в функции Формат(). Для этого в строке формата можно использовать параметр Л (Локализация):
// Принудительно выводим число по стандартам англоязычной локали (с точкой):
СтрокаАнгл = Формат(Выборка.Цена, "ЧДЦ=2; Л=en_US"); // Результат: "123.45"
// Принудительно выводим число по стандартам русскоязычной локали (с запятой):
СтрокаРус = Формат(Выборка.Цена, "ЧДЦ=2; Л=ru_RU"); // Результат: "123,45"
Подведем итог нашему подробному анализу проблемы. Для достижения идеального результата придерживайтесь простых правил:
ВЫРАЗИТЬ в запросах только для математического округления результатов расчетов (например, после деления или распределения сумм), чтобы исключить появление микрокопеек глубоко в дробной части, но не для визуального оформления.Формат(Значение, "ЧДЦ=2"), если для них критичен вывод дробной части.