При разработке и автоматизации различных бизнес-процессов в среде 1С часто возникает необходимость округлять числовые показатели не к стандартным целым значениям, а к определенному шагу или кратности. Например, при расчете логистических показателей (количество мест, упаковок, паллет) нередко требуется округление до "половинок" (шаг 0.5). При расчете цен или суммовых показателей в рознице часто требуется округлять значения до чисел, кратных 5 или 10 рублям.
В этой статье мы подробно разберем все возможные способы решения данной задачи: от простых математических формул на встроенном языке до использования встроенных механизмов БСП (Библиотеки Стандартных Подсистем) и округления непосредственно в запросах к базе данных.
Если перед нами стоит конкретная задача округлить число до ближайшего числа с дробной частью .0 или .5 (например, 2.4 превратить в 2.5, а 4.2 в 4.0), то нет необходимости использовать сложные алгоритмы. Достаточно применить базовые законы арифметики.
Рассмотрим логику этого процесса по шагам. Если мы умножим наше исходное число на 2, то шаг кратности 0.5 автоматически масштабируется до шага 1.0. После этого мы можем применить стандартную функцию платформы Окр() для округления до целых, а затем разделить полученный результат обратно на 2. Это вернет число в исходный масштаб, но уже с нужной точностью.
Посмотрим на пример практической реализации в коде 1С:
// Пример округления до половинок (0.5)
Число1 = 2.4;
Число2 = 4.2;
Число3 = 26.9;
Число4 = 44.7;
Результат1 = Окр(Число1 * 2, 0) / 2; // Вернет 2.5
Результат2 = Окр(Число2 * 2, 0) / 2; // Вернет 4.0
Результат3 = Окр(Число3 * 2, 0) / 2; // Вернет 27.0
Результат4 = Окр(Число4 * 2, 0) / 2; // Вернет 44.5
Данный метод работает чрезвычайно быстро и не требует вызова сторонних функций, что делает его идеальным решением для простых точечных расчетов.
Давайте проанализируем ситуацию, когда кратность округления может динамически меняться или когда требуется округлять до целых единиц кратности (например, до 5, 10, 50 рублей). Для этого мы можем расширить логику предыдущего метода до универсальной формулы:
Результат = Окр(Число / Кратность, 0) * Кратность;
Выясним причину работоспособности этой формулы. Мы сначала делим наше исходное число на шаг кратности, приводя задачу к поиску ближайшего целого числа "шагов". После стандартного округления функцией Окр() до целого значения мы умножаем результат обратно на величину шага.
Разберем по шагам пример работы этой формулы для округления до ближайшего кратного 5:
ШагКратности = 5;
// Пример 1: Число 2.4
// 2.4 / 5 = 0.48. Ближайшее целое к 0.48 — это 0.
Результат1 = Окр(2.4 / ШагКратности, 0) * ШагКратности; // Вернет 0
// Пример 2: Число 4.2
// 4.2 / 5 = 0.84. Ближайшее целое к 0.84 — это 1.
Результат2 = Окр(4.2 / ШагКратности, 0) * ШагКратности; // Вернет 5
// Пример 3: Число 26.9
// 26.9 / 5 = 5.38. Ближайшее целое к 5.38 — это 5.
Результат3 = Окр(26.9 / ШагКратности, 0) * ШагКратности; // Вернет 25
// Пример 4: Число 44.7
// 44.7 / 5 = 8.94. Ближайшее целое к 8.94 — это 9.
Результат4 = Окр(44.7 / ШагКратности, 0) * ШагКратности; // Вернет 45
Если в качестве переменной ШагКратности подставить 0.5, то формула отработает точно так же, как и специализированная функция из первого решения.
Если вы ведете разработку внутри современных типовых конфигураций (таких как 1С:ЗУП, 1С:ERP, 1С:Комплексная автоматизация или 1С:Управление торговлей), крайне рекомендуется использовать возможности встроенной Библиотеки Стандартных Подсистем. В них уже заложена готовая функция, которая не только выполняет математические расчеты, но и поддерживает различные правила округления (например, в большую или меньшую сторону).
Данная функция находится в общем модуле ЗарплатаКадрыClientСервер (или в аналогичных системных модулях расчетов). Рассмотрим код этой функции из типовой поставки:
Функция Округлить(Число, Точность = 0, Правило = Неопределено) Экспорт
Если Не ЗначениеЗаполнено(Число) Или Точность <= 0 Тогда
Возврат Число;
КонецЕсли;
Множитель = Число / Точность;
ЦелыйМножитель = Цел(Множитель);
Если Множитель = ЦелыйМножитель Тогда
Результат = Число;
Иначе
Результат = Точность * Окр(Множитель);
Если Правило = ПредопределенноеЗначение("Перечисление.ПравилаОкругленияПриРасчетеЗарплаты.ВБольшуюСторону") Тогда
Результат = Точность * (ЦелыйМножитель + 1);
ИначеЕсли Правило = ПредопределенноеЗначение("Перечисление.ПравилаОкругленияПриРасчетеЗарплаты.ВМеньшуюСторону") Тогда
Результат = Точность * ЦелыйМножитель;
КонецЕсли;
КонецЕсли;
Возврат Результат;
КонецФункции
Для применения этого метода достаточно просто вызвать его в вашей обработке или документе:
// Округляем число 2.4 до кратности 0.5
РезультатПоловинки = ЗарплатаКадрыКлиентСервер.Округлить(2.4, 0.5); // Результат: 2.5
// Округляем число 26.9 до кратности 5
РезультатПятерки = ЗарплатаКадрыКлиентСервер.Округлить(26.9, 5); // Результат: 25
В некоторых прикладных задачах стандартного математического округления (когда дробная часть округляется к ближайшему) бывает недостаточно. Например, при расчете количества коробок для упаковки товара, если у вас получилось 12.2 коробки, вам в любом случае понадобится 13 коробок (округление строго вверх), иначе товар не поместится.
Рассмотрим, как модифицировать наши алгоритмы для направленного округления до заданной кратности:
Цел():
Кратность = 5;
Число = 14.9;
РезультатВниз = Цел(Число / Кратность) * Кратность; // Вернет 10
Кратность = 5;
Число = 10.1;
Остаток = Число % Кратность;
РезультатВверх = ?(Остаток = 0, Число, (Цел(Число / Кратность) + 1) * Кратность); // Вернет 15
Очень часто обработку и округление данных необходимо выполнять не в цикле выборки на сервере приложений, а непосредственно при получении данных из базы данных СУБД с помощью запросов 1С.
Поскольку во встроенном языке запросов 1С функция Окр() недоступна, мы должны использовать конструкцию преобразования типов ВЫРАЗИТЬ(... КАК ЧИСЛО(X, 0)). Посмотрим, как это реализуется на практике.
Пример запроса для округления числового поля до кратности 5:
ВЫБРАТЬ
ВЫРАЗИТЬ(ИсходнаяТаблица.Количество / 5.0 КАК ЧИСЛО(15, 0)) * 5 КАК КоличествоКратноеПяти
ИЗ
Справочник.Номенклатура КАК ИсходнаяТаблица
Пример запроса для округления числового поля до кратности 0.5 (до половинок):
ВЫБРАТЬ
ВЫРАЗИТЬ(ИсходнаяТаблица.Количество * 2.0 КАК ЧИСЛО(15, 0)) / 2.0 КАК КоличествоКратноеПоловине
ИЗ
Справочник.Номенклатура КАК ИсходнаяТаблица
Важное правило: Обратите внимание, что при написании числовых литералов-делителей в запросе крайне желательно явно указывать их дробную часть (писать 5.0 вместо 5 и 2.0 вместо 2). Это помогает платформе и СУБД правильно определить конечную разрядность и тип данных результата операции деления, предотвращая неявное усечение до целых чисел в процессе расчета.
При работе с дробными шагами (например, 0.5, 0.05 и т.д.) разработчики могут столкнуться с аппаратными погрешностями представления вещественных чисел стандарта IEEE 754. На уровне памяти компьютера выражение 2.4 / 0.5 может вычисляться не как ровное число 4.8, а иметь вид 4.7999999999999998.
В пограничных ситуациях функция Окр() из-за этого может выдать неверный результат. Чтобы полностью исключить вероятность такой системной ошибки, рекомендуется использовать "двойное" округление с предварительной нормализацией разрядности:
ЧислоДляОкругления = 2.4;
ШагКратности = 0.5;
// Сначала округляем промежуточный результат до заведомо высокой точности (10 знаков),
// чтобы убрать "шум" дробной точности процессора, а затем округляем до целых шагов.
ПромежуточныйРезультат = ЧислоДляОкругления / ШагКратности;
НормализованныйРезультат = Окр(ПромежуточныйРезультат, 10);
ИтоговоеЗначение = Окр(НормализованныйРезультат, 0) * ШагКратности; // Надежный результат: 2.5
Используя этот паттерн, вы защитите свои математические алгоритмы от случайных расхождений в копейках при работе с большими объемами расчетных данных.