К статье
Практический материал

Разработка и Конфигурация · 12 мин

Как обрезать число без округления в 1С

Как в 1С обрезать число без округления: функция Цел(), приёмы через Окр() и Цел() для дробной части, отсечение разрядов, типичные ошибки и примеры кода.

Во встроенном языке 1С нет отдельной функции «обрезать», которая просто отбрасывает дробную часть или лишние разряды. Стандартная функция Окр() всегда округляет по математическим правилам: 12,7 превратится в 13, а 12,49 — в 12. Когда нужно именно отсечь хвост — например, получить 12 из 12,99 или 3,14 из 3,14159 — приходится комбинировать имеющиеся средства.

Задача возникает чаще, чем кажется: при расчёте скидок с отсечением копеек, при распределении сумм по строкам документа, при расчёте целого количества упаковок, при формировании ценников, где копейки должны отбрасываться вниз, а не округляться. Ошибка в выборе метода приводит к расхождениям в копейках, которые потом всплывают в отчётах и при сверке с контрагентами.

Ниже разобраны рабочие приёмы для встроенного языка 1С: функция Цел(), комбинации с Окр() и делением, отсечение разрядов целой части, а также обработка отрицательных чисел. Все примеры применимы как в тонком клиенте, так и на сервере, в любой типовой и самописной конфигурации.

Отсечение дробной части функцией Цел()

Самый простой случай — нужно получить целую часть числа, отбросив всё после запятой. Для этого во встроенном языке 1С есть функция Цел(). Она возвращает целую часть числа, просто отбрасывая дробную составляющую, без всякого округления.

Число = 123,99;

Результат = Цел(Число); // 123

Число2 = 7,01;

Результат2 = Цел(Число2); // 7

Обратите внимание: Окр(123,99) дал бы 124, потому что округляет по правилам математики. Именно поэтому заменять Цел() на Окр() нельзя, если бизнес-логика требует отсечения. Типичный пример — расчёт количества полных упаковок: из 123,99 условных единиц товара можно собрать только 123 полных упаковки, а не 124.

Функция работает и в запросах 1С: конструкция ВЫРАЗИТЬ(Поле КАК ЧИСЛО(15, 0)) тоже округляет, а вот приведение к целому через вычисления в запросе потребует тех же приёмов, что и в коде. Поэтому часто проще выгрузить результат запроса и обработать числа в цикле на встроенном языке.

Обрезание до заданного количества знаков после запятой

Когда нужно оставить, например, два знака после запятой, но не округлять третий, используется классический приём: умножить число на 10 в нужной степени, взять целую часть и разделить обратно. Так из 3,14159 получится 3,14, а не 3,14 с округлением до 3,14 — разница видна на числах вроде 3,14999, где округление дало бы 3,15.

Функция ОбрезатьЧисло(Число, ЗнаковПослеЗапятой)

Множитель = Pow(10, ЗнаковПослеЗапятой);

Возврат Цел(Число * Множитель) / Множитель;

КонецФункции

// Примеры:

// ОбрезатьЧисло(3,14999; 2) = 3,14

// ОбрезатьЧисло(99,999; 2) = 99,99

// ОбрезатьЧисло(15,567; 1) = 15,5

Вместо Pow(10, N) можно написать литерал, если разрядность фиксирована: Цел(Сумма * 100) / 100 для копеек. Такой вариант чуть быстрее и читабельнее в простых случаях. Для денежных сумм это самый частый сценарий: отсечь всё, что меньше копейки, при расчёте скидок или комиссий.

bsl
Функция ОбрезатьБезОкругления(Число, Разрядность)
	Множитель = Pow(10, Разрядность);
	Возврат Цел(Число * Множитель) / Множитель;
КонецФункции

Есть ограничение по точности: тип «Число» в 1С хранит ограниченное количество значащих цифр (по умолчанию длина и точность задаются в метаданных, а в вычислениях действуют внутренние пределы платформы). При умножении очень большого числа на большой множитель возможна потеря младших разрядов ещё до отсечения. Для денежных величин в разумных диапазонах это не проблема, но для научных расчётов приём стоит проверять на граничных значениях.

Отсечение разрядов целой части (до десятков, сотен, тысяч)

Обратная задача — обрезать число «слева от запятой»: получить из 17 849 сумму 17 800 или 17 000. Это нужно при уценке, установке «круглых» цен вниз, расчёте бюджетных лимитов. Приём зеркальный: делим на степень десяти, берём целую часть, умножаем обратно.

// Обрезать до сотен вниз

Цена = 17849;

ЦенаОбрезанная = Цел(Цена / 100) * 100; // 17800

// Обрезать до тысяч вниз

ЦенаОбрезанная = Цел(Цена / 1000) * 1000; // 17000

Здесь важно не перепутать порядок операций: сначала деление, потом Цел(), потом умножение. Если написать Цел(Цена) / 100 * 100, ничего не изменится, потому что целая часть и так не содержит дроби, а деление и умножение снова вернут исходное значение с дробным остатком в промежуточном результате, который потом уйдёт в итог.

Отрицательные числа: где прячется подвох

Функция Цел() отбрасывает дробную часть в сторону нуля: Цел(-3,7) вернёт -3, а не -4. Математически это усечение (truncation), а не округление вниз (floor). Для многих учётных задач разницы нет, но если логика требует «всегда в меньшую сторону по числовой оси», для отрицательных значений поведение будет неожиданным.

Если нужен именно floor (округление вниз по значению), добавьте корректировку:

Функция ОкруглитьВниз(Число)

Результат = Цел(Число);

Если Число < 0 И Число <> Результат Тогда

Результат = Результат - 1;

КонецЕсли;

Возврат Результат;

КонецФункции

// ОкруглитьВниз(-3,7) = -4

// ОкруглитьВниз(3,7) = 3

Перед внедрением любого варианта уточните в постановке задачи, как должна обрезаться отрицательная сумма: возврат клиенту -123,99 — это -123 или -124 рубля? Бухгалтерия и разработчик часто понимают «обрезать» по-разному, и расхождение вылезет именно на возвратах и сторно.

Сравнение вариантов

Цел(Число)

Отсечение дробной части в сторону нуля. Подходит для положительных сумм, количества упаковок. Для отрицательных даёт усечение, а не floor.

Цел(Число * 100) / 100

Обрезание до копеек без округления. Стандартный приём для скидок и комиссий. Требует контроля точности при больших числах.

Окр(Число, 2, РежимОкругления.Окр15как10)

Это округление, а не обрезание. Используйте, только если методика явно требует округления, иначе получите расхождения в копейках.

Строковые функции (Лев, СтрНайти)

Обрезание через преобразование в строку. Работает, но медленнее, зависит от регионального разделителя дробной части и считается плохой практикой.

Почему не стоит обрезать число через строку

Встречающийся на форумах способ — преобразовать число в строку, найти позицию запятой и взять нужное количество символов функцией Лев(). Технически это работает, но создаёт три проблемы разом.

Во-первых, разделитель дробной части в строковом представлении зависит от региональных настроек сеанса: где-то это точка, где-то запятая. Код, работавший у разработчика, может сломаться на сервере с другими настройками. Во-вторых, при обратном преобразовании строки в число через Число() неочевидные форматы (пробелы-разделители групп разрядов) вызывают ошибки. В-третьих, строковые операции заметно медленнее арифметики в массовых обработках — например, при пересчёте миллиона строк табличной части.

Типичные ошибки
01
Использовать Окр() вместо Цел() — итоговая сумма документа расходится с суммой строк на копейки, потому что округление увеличивает значения
02
Игнорировать отрицательные числа — на возвратах и сторно обрезание ведёт себя иначе, чем ожидал заказчик
03
Обрезать через строку — код ломается при смене региональных настроек сервера и работает медленно в циклах
04
Умножать на 100 после Цел() вместо того чтобы делить — при обрезании до сотен порядок операций критичен
05
Забыть, что в запросе ВЫРАЗИТЬ(... КАК ЧИСЛО) округляет, а не обрезает — результат запроса не совпадает с расчётом в коде

Применение в типовых сценариях

Расчёт скидки с отсечением копеек

Торговая компания хочет, чтобы скидка всегда считалась в пользу компании: дробные копейки отбрасываются. Формула: Скидка = Цел(СуммаДокумента ПроцентСкидки) / 100, если процент задан как доля с копеечной точностью, либо Цел(СуммаДокумента Процент / 100 * 100) / 100 для отсечения результата до копеек. Главное — зафиксировать правило в регламенте, чтобы при претензиях клиентов было на что сослаться.

Количество целых упаковок

При отгрузке товара, продающегося только коробами, нужно знать, сколько полных упаковок выйдет из заказанного количества: КоличествоКоробов = Цел(КоличествоЗаказано / КоличествоВУпаковке). Остаток считается отдельно через оператор % (остаток от деления): ОстатокШтук = КоличествоЗаказано % КоличествоВУпаковке.

Распределение суммы по строкам

При распределении общей суммы пропорционально базе последняя строка обычно получает «хвост» как разницу между итогом и уже распределённым. Если промежуточные строки обрезать, а не округлять, хвост станет больше, чем при округлении, — это нормально, но об этом стоит знать, чтобы не принять корректное поведение за ошибку.

Порядок внедрения обрезания в расчёт
  1. 1
    Определите требуемую разрядность: копейки (2 знака), рубли (0), сотни или тысячи
  2. 2
    Уточните у постановщика поведение для отрицательных сумм: усечение к нулю или строго вниз
  3. 3
    Напишите универсальную функцию в общем модуле вместо разрозненных формул по коду
  4. 4
    Прогоните граничные значения: 0,999; -0,001; 99,999; суммы с тремя знаками после запятой
  5. 5
    Сверьте итог документа с суммой обрезанных строк и зафиксируйте правило распределения разницы

Проверка результата и граничные случаи

Перед тем как закрыть задачу, полезно прогнать функцию на контрольном наборе значений. Минимальный набор: положительное число с «длинным» хвостом (3,14999), значение ровно на границе (3,14), отрицательное число (-3,14999), ноль, очень малое дробное (0,001 при обрезании до двух знаков — должен получиться 0).

Отдельно проверьте числа, у которых после умножения на множитель дробная часть состоит из девяток: из-за особенностей двоичного представления дробей 1,005 100 может дать 100,49999..., и Цел() вернёт 100 вместо ожидаемых 100 (здесь как раз совпадение) — но в соседних случаях возможны сюрпризы. Если точность критична, рассмотрите вариант с небольшим смещением: Цел(Число Множитель + 0,0000001) / Множитель — но только после согласования, потому что это уже гибрид обрезания и округления.

Контрольные точки

Как проверить результат

Цел(12,99)
Норма

12

Тревожный признак

13 — значит, использована функция Окр() вместо Цел()

Обрезание -5,7
Норма

-5 при усечении или -6 при floor — по согласованному правилу

Тревожный признак

Результат не совпадает с регламентом на возвратах

Сумма строк после обрезания
Норма

Итог меньше или равен исходной сумме, разница зафиксирована

Тревожный признак

Итог больше исходного — где-то сработало округление вверх

Поведение на сервере и на клиенте
Норма

Результаты идентичны

Тревожный признак

Различаются — вероятно, использовано строковое преобразование, зависящее от региональных настроек

Удобно один раз написать функции ОбрезатьЧисло() и ОкруглитьВниз() в общем серверном модуле и запретить команде использовать «голые» конструкции Цел(Х*100)/100 по коду. Это упрощает ревью и позволяет централизованно поменять правило, если бухгалтерия пересмотрит методику.
ПЗ
Практическое замечание

Частые вопросы

Чем Цел() отличается от Окр() в 1С?

Цел() просто отбрасывает дробную часть: Цел(12,99) = 12. Окр() округляет по математическим правилам: Окр(12,99) = 13, Окр(12,49) = 12. Для отсечения без округления подходит только Цел() и производные приёмы.

Как обрезать число до двух знаков после запятой без округления?

Используйте формулу Цел(Число * 100) / 100. Например, из 99,999 получится 99,99, а из 3,14159 — 3,14. Для другой разрядности замените 100 на 10 в нужной степени.

Почему Цел() для отрицательного числа даёт «неправильный» результат?

Цел() усекает в сторону нуля: Цел(-3,7) = -3. Если требуется округление строго вниз (floor), нужно для отрицательных нецелых чисел вычитать единицу из результата Цел(). Какое поведение правильное — определяет методика расчёта, а не платформа.

Можно ли обрезать число прямо в запросе 1С?

Отдельной функции усечения в языке запросов нет, а ВЫРАЗИТЬ(... КАК ЧИСЛО(15,2)) округляет. Обычно значения выбирают запросом как есть, а обрезают уже при обходе результата на встроенном языке. Альтернатива — арифметика с умножением и делением прямо в полях запроса, но она ухудшает читаемость.

Что делать, если после обрезания сумма строк не сходится с итогом документа?

Это ожидаемо: обрезание всегда уменьшает промежуточные значения. Стандартное решение — доначислить накопленную разницу на последнюю строку распределения или выделить её в отдельную строку «округление». Правило нужно зафиксировать в регламенте расчёта.

Есть ли готовая функция обрезания в БСП (библиотека стандартных подсистем)?

В БСП нет отдельной публичной функции «обрезать без округления» — там в основном встречаются функции округления и форматирования чисел. Практичнее написать собственную функцию в общем модуле проекта: она занимает три строки и не создаёт зависимости от версии библиотеки.

Итог

Что важно запомнить

Обрезание без округления в 1С делается через Цел(): для дробной части — напрямую, для знаков после запятой — через умножение и деление на 10^N, для разрядов целой части — в обратном порядке. Отдельно согласуйте поведение на отрицательных числах и правило распределения копеечной разницы, а готовую функцию вынесите в общий модуль.