Обрезка чисел в 1С 8.3: Окр, Цел, Формат и запросы

· 9 мин

Содержание

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

Ниже — каждый способ с примерами кода на встроенном языке 1С:Предприятие 8.3, обрезка прямо в запросе и разбор того, почему иногда число округляется само, без единой вашей строчки кода.

Три задачи, которые называют «обрезкой»

Прежде чем писать код, определитесь, что именно нужно. Три операции легко перепутать, а результат у них разный.

Что нужноФункцияЧто возвращаетПример
Округлить по математикеОкр()числоОкр(3.146, 2) → 3.15
Отбросить дробную частьЦел()числоЦел(3.9) → 3
Усечь без округления до N знаковЦел() + делениечисло3.149 → 3.14
Вывести для показаФормат()строку"3,15 руб."
Ограничить разрядность в запросеВЫРАЗИТЬчислоЧИСЛО(15, 2)

Главное правило: для расчётов используйте Окр() или Цел(), а Формат() оставьте только для вывода. Если результат участвует в дальнейших вычислениях — это не задача форматирования.

Что вам нужно сделать с числом?

Округление функцией Окр()

Окр() — основной способ привести число к нужной разрядности. Синтаксис:

Окр(<Число>, <Разрядность>, <Режим>)

Разрядность — количество знаков после запятой. Несколько примеров:

Окр(3.14159, 2)     // 3.14
Окр(3.146, 2)       // 3.15
Окр(127.5, 0)       // 128

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

Окр(1234.56, -2)    // 1200  — округление до сотен
Окр(1234.56, -3)    // 1000  — округление до тысяч
Окр(1250, -2)       // 1300  — половина уходит вверх

Третий параметр — режим округления «ровной половины» (когда отбрасываемая часть равна ровно 0,5). По умолчанию половина уходит от нуля: Окр(2.5) даёт 3. Если нужно округлять половину к нулю (2.5 → 2), передайте режим РежимОкругления.Окр15как10:

Окр(2.5, 0, РежимОкругления.Окр15как20)   // 3  — половина от нуля (по умолчанию)
Окр(2.5, 0, РежимОкругления.Окр15как10)   // 2  — половина к нулю

Если вы ведёте разработку на англоязычном варианте синтаксиса, та же функция называется Round() — это не отдельная функция, а второе имя Окр(). Так же Цел() превращается в Int(), а Формат() в Format().

Где обрезка суммы ловит копейку

Самая частая практическая задача на округление — распределить сумму по нескольким строкам. Здесь обрезка каждой части по отдельности даёт расхождение.

Пример: 100 руб. делим на 3 строки.

100 / 3 = 33,3333…
Окр(33.3333, 2) = 33,33
33,33 × 3 = 99,99   // потеряли копейку

Правильный приём — округлять все части, кроме последней, а последнюю считать как остаток:

Сумма  = 100;
Часть  = Окр(Сумма / 3, 2);     // 33,33
Строка1 = Часть;                 // 33,33
Строка2 = Часть;                 // 33,33
Строка3 = Сумма - Часть * 2;     // 100 - 66,66 = 33,34

Итог снова сходится к 100,00. Тот же принцип типовые конфигурации применяют при распределении НДС и скидок по строкам документа.

Отбросить дробную часть: Цел()

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

Цел(3.9)     // 3
Цел(3.1)     // 3
Цел(-3.9)    // -3   — усечение идёт к нулю, не вниз

Обратите внимание на отрицательные: Цел(-3.9) даёт -3, а не -4. Это не округление вниз (floor), а именно отбрасывание дробной части. Для положительных чисел Цел() и округление вниз совпадают, а для отрицательных — расходятся на единицу.

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

// округление вниз, работает и для отрицательных
Вниз = ?(Число < 0 И Число <> Цел(Число), Цел(Число) - 1, Цел(Число));
// -3.9 → -4, 3.9 → 3

Усечь до N знаков без округления

У Цел() нет параметра разрядности, но нужный эффект даёт простой приём — домножить, обрезать, разделить обратно:

// усечь до 2 знаков строго вниз
Результат = Цел(Число * 100) / 100;

// для 3.149 получим 3.14, а не 3.15

Разница с Окр() принципиальна: Окр(3.149, 2) вернёт 3.15 (округлит), а Цел(3.149 * 100) / 100 вернёт 3.14 (отбросит). Для сумм налогов и цен это может давать расхождение в копейку — выбирайте осознанно.

Округление всегда вверх

Отдельной функции «округлить вверх» в языке нет. Для положительных чисел её собирают из Цел():

ВверхДоЦелого = Цел(Число) + ?(Число > Цел(Число), 1, 0);
// 3.1 → 4, 3.0 → 3

Форматирование числа для вывода: Формат()

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

Формат(1234.5, "ЧДЦ=2")          // "1 234,50"
Формат(1234.5, "ЧДЦ=2; ЧГ=0")    // "1234,50"  — без разделителя групп
Формат(1234567, "ЧГ=' '")        // "1 234 567" — пробел между тысячами

Основные параметры строки форматирования:

ПараметрЧто задаётПример
ЧДЦзнаков после запятойЧДЦ=2 → 12,30
ЧГразделитель групп разрядов (0 — убрать)ЧГ=0 → 1234
ЧРДсимвол-разделитель дробной частиЧРД=. → 12.30

Параметры разделяются точкой с запятой и комбинируются в одной строке:

Формат(1234.5, "ЧДЦ=2; ЧГ=0")       // "1234,50"
Формат(1234.5, "ЧДЦ=2; ЧРД=.")      // "1 234.50"

Формат() не меняет само число и не годится для расчётов. Результат — текст. Попытка сложить его с числом либо вызовет ошибку, либо приведёт к неявному преобразованию с неожиданным результатом. Если число нужно именно округлить для дальнейших вычислений — это задача Окр(), а не Формат().

Почему число обрезается само: тип «Число» и его точность

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

Если у реквизита справочника, ресурса регистра или реквизита документа задана точность 0, то при записи значения 3,7 в базе окажется 4. Платформа сама приводит число к точности, указанной в метаданных, — независимо от того, что вы написали в коде.

Если «обрезка» в коде работает, а в базе всё равно лишние или недостающие знаки — проверьте точность реквизита в конфигураторе. Код тут ни при чём.

Ещё одно следствие: число в 1С хранится как десятичное с фиксированной точкой, а не как двоичное с плавающей (как в большинстве языков). Поэтому классической ловушки 0.1 + 0.2 ≠ 0.3 в 1С нет — сложение денежных сумм ведёт себя предсказуемо.

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

Обрезка числа в запросе

Если число нужно привести к разрядности прямо в тексте запроса, не выгружая в код, применяют оператор ВЫРАЗИТЬ:

ВЫБРАТЬ
    ВЫРАЗИТЬ(Продажи.Сумма КАК ЧИСЛО(15, 2)) КАК Сумма
ИЗ
    РегистрНакопления.Продажи КАК Продажи

ЧИСЛО(15, 2) означает: до 15 знаков всего, из них 2 после запятой. Лишние дробные разряды при этом округляются, а не отбрасываются. Если нужна именно усечённая вниз сумма — выгрузите значение в код и примените приём с Цел() оттуда: в языке запросов функции отбрасывания дробной части нет.

Отрицательная разрядность в запросе не задаётся — округление до сотен и тысяч делается уже в коде обработки результата.

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

Через строковые функции (и когда это оправдано)

Иногда встречается экзотический способ — превратить число в строку, найти запятую и отрезать всё после неё:

Текст = Строка(Число);
ПозицияЗапятой = СтрНайти(Текст, ",");
ЦелаяЧасть = Число(Лев(Текст, ПозицияЗапятой - 1));

Этот путь хрупкий: результат Строка() зависит от региональных настроек (разделитель может быть точкой или запятой, могут появиться пробелы-разделители групп), а лишние преобразования число → строка → число замедляют код. Для обрезки чисел он не нужен — с задачей полностью справляются Окр() и Цел().

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

Частые ошибки при обрезке чисел

Диагностика: выберите симптом

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

В чём разница между Цел() и Окр() с нулевой разрядностью?
Цел(2.9) вернёт 2 — дробная часть просто отбрасывается. Окр(2.9) вернёт 3 — число округляется по математическим правилам. На отрицательных разница заметнее: Цел(-2.9) = -2, а Окр(-2.9) = -3.
Почему результат Формат() нельзя сложить с числом?
Потому что Формат() возвращает строку, а не число. Если результат нужен для вычислений, применяйте Окр(), а форматирование используйте только на последнем шаге — при выводе.
Как усечь число до двух знаков строго вниз, без округления?
Цел(Число * 100) / 100. Для 3.149 получите 3.14, для 3.141 — тоже 3.14. В отличие от Окр(3.149, 2), которая вернёт 3.15.
Почему при записи 3,7 в регистр в базе оказывается 4?
У ресурса или реквизита задана точность 0 знаков. Платформа округляет значение до точности, указанной в конфигураторе, независимо от кода. Поменять можно только в конфигураторе.
Есть ли в языке 1С округление всегда вверх?
Отдельной функции нет. Для положительных чисел: Цел(Число) + ?(Число > Цел(Число), 1, 0). Для отрицательных логику придётся учитывать по знаку отдельно.