Обрезка чисел в 1С 8.3: Окр, Цел, Формат и запросы
Содержание
«Обрезать число» в 1С означает одну из трёх разных операций, и для каждой — своя функция. Округлить до нужного знака — Окр(). Отбросить дробную часть без округления — Цел(). Красиво вывести на экран или в печатную форму — Формат(). Выбор не косметический: Окр() и Цел() возвращают число, с которым можно считать дальше, а Формат() отдаёт строку, и в арифметике она уже не участвует.
Ниже — каждый способ с примерами кода на встроенном языке 1С:Предприятие 8.3, обрезка прямо в запросе и разбор того, почему иногда число округляется само, без единой вашей строчки кода.
Три задачи, которые называют «обрезкой»
Прежде чем писать код, определитесь, что именно нужно. Три операции легко перепутать, а результат у них разный.
| Что нужно | Функция | Что возвращает | Пример |
|---|---|---|---|
| Округлить по математике | Окр() | число | Окр(3.146, 2) → 3.15 |
| Отбросить дробную часть | Цел() | число | Цел(3.9) → 3 |
| Усечь без округления до N знаков | Цел() + деление | число | 3.149 → 3.14 |
| Вывести для показа | Формат() | строку | "3,15 руб." |
| Ограничить разрядность в запросе | ВЫРАЗИТЬ | число | ЧИСЛО(15, 2) |
Главное правило: для расчётов используйте Окр() или Цел(), а Формат() оставьте только для вывода. Если результат участвует в дальнейших вычислениях — это не задача форматирования.
Окр(Число, Разрядность). Результат — число, округлённое по математическим правилам.Цел(). Для усечения до N знаков: Цел(Число * 10^N) / 10^N.Формат(). Результат — строка, в арифметике не участвует.ВЫРАЗИТЬ(Поле КАК ЧИСЛО(длина, точность)).Округление функцией Окр()
Окр() — основной способ привести число к нужной разрядности. Синтаксис:
Окр(<Число>, <Разрядность>, <Режим>)
Разрядность — количество знаков после запятой. Несколько примеров:
Окр(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));
Этот путь хрупкий: результат Строка() зависит от региональных настроек (разделитель может быть точкой или запятой, могут появиться пробелы-разделители групп), а лишние преобразования число → строка → число замедляют код. Для обрезки чисел он не нужен — с задачей полностью справляются Окр() и Цел().
Регулярные выражения для обрезки чисел тоже избыточны: разбирать число как текст ради отбрасывания дробной части — решение сложнее самой задачи. Строковый разбор оправдан в одном случае — когда на входе уже строка (например, из внешнего файла или веб-запроса) и число из неё только предстоит получить. Но и тогда правильнее сначала аккуратно привести строку к числу через Число(), предварительно заменив разделитель на нужный, а уже потом применить Окр() или Цел() к полученному числу — а не резать текст по символам.
Частые ошибки при обрезке чисел
Формат() возвращает строку. Для вычислений берите Окр() или Цел(), а Формат() оставьте для вывода на экран.Цел(3.9) — это 3, а не 4. Цел() отбрасывает дробную часть, не округляет. Нужно округление — берите Окр().Цел(-3.9) даёт -3 (к нулю), а не -4 (вниз). Для округления вниз отрицательных это разные результаты — учитывайте знак.Частые вопросы
В чём разница между Цел() и Окр() с нулевой разрядностью?
Цел(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?
Есть ли в языке 1С округление всегда вверх?
Цел(Число) + ?(Число > Цел(Число), 1, 0). Для отрицательных логику придётся учитывать по знаку отдельно.