Как получить дробную часть числа во встроенном языке 1С

· 9 мин

Содержание

Дробную часть числа в 1С выделяют одной строкой: ДробнаяЧасть = Число - Цел(Число);. Функция Цел() отбрасывает всё после запятой, а разница с исходным значением и есть дробная часть: для 12.75 получится 0.75.

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

Функция Цел(): основной способ отделить целую и дробную часть

Цел(Число) возвращает целую часть, просто отрезая дробную. Она не округляет: Цел(9.99) вернёт 9, а не 10.

Число = 12.75;

ЦелаяЧасть   = Цел(Число);           // 12
ДробнаяЧасть = Число - ЦелаяЧасть;   // 0.75

Почему это надёжно:

  • 🔹 тип Число в 1С хранит значения в десятичной форме, поэтому 12.75 - 12 даёт ровно 0.75, без «хвостов» вида 0.7500000001, привычных по другим языкам;
  • 🔹 функция есть и на клиенте, и на сервере, директивы компиляции не мешают;
  • 🔹 результат имеет тот же знак, что и исходное число (подробнее — в разделе про отрицательные значения).

Если дробную часть нужно получить как целое число (например, 75 из 12.75), умножьте её на степень десятки:

ДробнаяЧасть = Число - Цел(Число);   // 0.75
ДвеЦифры     = ДробнаяЧасть * 100;   // 75

Проверка «число целое?» — частный случай той же операции: Если Число = Цел(Число) Тогда. Это короче и понятнее, чем сравнивать дробную часть с нулём.

Округл(): когда дробную часть надо округлить, а не отрезать

Округл(Число, Разрядность, РежимОкругления) не выделяет дробную часть сам по себе. Но без него не обойтись, когда дробная часть нужна с заданной точностью или когда исходное значение «грязное».

Параметры функции:

ПараметрЧто задаётПример
Числоисходное значение12.756
Разрядностьсколько знаков после запятой оставить; может быть отрицательной2 → 12.76, -1 → 10
РежимОкруглениякак округлять половинуРежимОкругленияЧисла.Окр15как20

По умолчанию действует Окр15как20: половина округляется от нуля, 2.5 → 3. Режим Окр15как10 округляет половину к нулю: 2.5 → 2.

Типовая связка «округлить, потом выделить»:

Число = 12.756;

Число        = Округл(Число, 2);         // 12.76
ДробнаяЧасть = Число - Цел(Число);       // 0.76

Почему порядок важен. Если сначала отрезать, а потом округлять дробную часть отдельно, на числах вроде 12.997 получите 0.997 → 1.00. Дробная часть «переполнится» и станет равна единице, а целая останется 12. Округляйте исходное число, а не кусок.

Через Округл() можно получить и целую часть с округлением, если отбрасывание не подходит:

ЦелаяОкругленная = Округл(12.6);   // 13
ЦелаяОтрезанная  = Цел(12.6);      // 12

Остаток от деления на единицу: оператор %

Во встроенном языке 1С есть оператор остатка от деления %. Он работает и с дробными операндами, поэтому остаток от деления на 1 — это дробная часть:

ДробнаяЧасть = 12.75 % 1;   // 0.75

Результат совпадает с Число - Цел(Число), включая знак: у -12.75 % 1 тоже будет -0.75.

Где % удобнее, чем Цел():

  • 🔸 дробная часть относительно шага. Число % 0.05 показывает, насколько цена отклоняется от кратности пяти копейкам;
  • 🔸 отдельная цифра после запятой. Вторая цифра числа 12.75: Цел(12.75 * 100) % 10 → 5;
  • 🔸 проверка кратности. Количество % 1 = 0 — количество целое, Количество % 6 = 0 — делится на упаковки по 6 штук.

Цифры дробной части по отдельности

Иногда нужна не вся дробь, а конкретная цифра — например, для контрольного разряда или проверки, что в количестве нет третьего знака. Сдвиньте нужную цифру в позицию единиц и возьмите остаток от деления на 10:

Функция ЦифраПослеЗапятой(Число, Позиция)
    Сдвинуто = Цел(Число * Pow(10, Позиция));
    Возврат Сдвинуто % 10;
КонецФункции

ЦифраПослеЗапятой(12.758, 1)   // 7
ЦифраПослеЗапятой(12.758, 3)   // 8

Проверка: 12.758 * 1000 = 12758, остаток от деления на 10 — 8. Для отрицательных чисел результат тоже будет отрицательным, поэтому передавайте в функцию модуль.

Шаг цены

Округление цены до шага строится на той же идее, но через Округл():

Шаг  = 0.05;
Цена = 103.27;

ЦенаКратная = Округл(Цена / Шаг) * Шаг;   // 103.25
Отклонение  = Цена % Шаг;                 // 0.02

Проверим арифметику: 103.27 / 0.05 = 2065.4, округление даёт 2065, умножение на 0.05 — 103.25. Разница с исходной ценой — 0.02.

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

МетодКодКогда братьРиски
Вычитание целой частиЧ - Цел(Ч)почти всегдадлинный хвост после деления
Остаток от деленияЧ % 1шаг, кратность, отдельные цифрынепривычен для читающих код
Округлить, затем вычестьЧ - Цел(Ч) после Округл(Ч, 2)суммы и ценызабыть, что округлено исходное число
Через строкуСтрРазделить(Строка(Ч), ",")никогдаразделитель и пробелы зависят от локали

Последняя строка таблицы — частая ошибка новичков. Строка(1234.5) вернёт 1 234,5: с неразрывным пробелом между разрядами и запятой, которая на другой локали может стать точкой. Разбор такой строки ломается при первом же переносе базы на другой сервер.

Отрицательные числа и граничные случаи

Знак дробной части

Цел() отбрасывает дробь в сторону нуля, а не вниз:

Цел(-3.7)          // -3, а не -4
-3.7 - Цел(-3.7)   // -0.7

Если вам нужна «математическая» дробная часть, всегда неотрицательная (-3.7 → 0.3), считайте целую часть с округлением вниз:

Функция ЦелаяЧастьВниз(Число)
    Результат = Цел(Число);
    Если Число < 0 И Число <> Результат Тогда
        Результат = Результат - 1;
    КонецЕсли;
    Возврат Результат;
КонецФункции

ЦелаяЧасть   = ЦелаяЧастьВниз(-3.7);   // -4
ДробнаяЧасть = -3.7 - ЦелаяЧасть;      // 0.3

Если нужен только модуль дробной части без знака, достаточно ?(ДробнаяЧасть < 0, -ДробнаяЧасть, ДробнаяЧасть).

Результат деления

10 / 3 даёт 3.333… с десятками троек. Дробная часть такого числа тоже бесконечная, и сравнивать её с 0.33 бессмысленно. Больше того, 1 / 3 * 3 может дать не ровно 1, а число на хвостик меньше — и Цел() вернёт 0.

Перед Цел() от результата деления округляйте его до нужной точности.

Результат = Округл(Сумма / Количество, 2);

Значения не числового типа

Цел() от Неопределено, Null или строки вызовет ошибку преобразования типа. Так бывает с пустыми реквизитами составного типа и с полями запроса после левого соединения.

Если ТипЗнч(Значение) = Тип("Число") Тогда
    ДробнаяЧасть = Значение - Цел(Значение);
Иначе
    ДробнаяЧасть = 0;
КонецЕсли;

Строку из файла сначала превратите в число: Число(СтрЗаменить(Текст, ",", ".")). Конструкцию оберните в Попытка … Исключение, если в данных может встретиться мусор.

Точность реквизита

Реквизит с типом Число(15, 2) хранит только два знака после запятой. Записали туда 12.756 — в базе окажется значение с двумя знаками. Не полагайтесь на это неявное приведение: округлите сами через Округл(), тогда результат будет одинаковым в коде и в базе.

Рубли и копейки: практический пример

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

Функция РазложитьСумму(Знач Сумма)
    Сумма   = Округл(Сумма, 2);
    Рубли   = Цел(Сумма);
    Копейки = (Сумма - Рубли) * 100;

    Результат = Новый Структура;
    Результат.Вставить("Рубли", Рубли);
    Результат.Вставить("Копейки", Копейки);
    Возврат Результат;
КонецФункции

Части = РазложитьСумму(1234.56);
// Части.Рубли = 1234, Части.Копейки = 56

Разберём ловушки по шагам:

  1. Округление в начале. Без него сумма 10.999 дала бы 10 рублей и 99.9 копейки. С округлением получается 11.00 → 11 рублей 0 копеек.
  2. Копейки после округления — целые. (1234.56 - 1234) * 100 = 56, дополнительный Цел() не нужен.
  3. Вывод с ведущим нулём. Пять копеек должны печататься как 05, а не 5:
ТекстКопеек = Формат(Части.Копейки, "ЧЦ=2; ЧВН=; ЧН=00");
ТекстРублей = Формат(Части.Рубли, "ЧГ=0; ЧН=0");
Текст = ТекстРублей + " руб. " + ТекстКопеек + " коп.";
// 1234 руб. 56 коп.

Параметр ЧВН включает ведущие нули, ЧН задаёт текст для нуля, ЧГ=0 убирает разделитель разрядов.

  1. Возвраты и сторно. Для -15.30 функция вернёт -15 рублей и -30 копеек. В печатной форме знак обычно выводят один раз перед суммой, поэтому раскладывайте модуль суммы, а знак добавляйте отдельно.

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

Массовые расчёты: где считать и как не тормозить

Сама операция Число - Цел(Число) дешёвая. Медленно становится из-за того, что её окружает.

В запросе вместо цикла

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

ВЫБРАТЬ
    Остатки.Номенклатура,
    Остатки.КоличествоОстаток КАК Количество,
    Остатки.КоличествоОстаток
        - ВЫРАЗИТЬ(Остатки.КоличествоОстаток - 0.5 КАК ЧИСЛО(15, 0)) КАК ДробнаяЧасть
ИЗ
    РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки

Сдвиг на 0.5 превращает округление в отбрасывание вниз: 7.3 - 0.5 = 6.8 → 7, 7.0 - 0.5 = 6.5 → 7. Приём рассчитан на неотрицательные значения. Для отрицательного целого числа -3 - 0.5 = -3.5, и результат зависит от того, как округляется половина, — такие строки отфильтруйте условием или посчитайте в коде.

В свежих версиях платформы в язык запросов добавлены математические функции, включая ЦЕЛ и ОКР. Откройте синтакс-помощник своей версии: если они там есть, используйте их вместо трюка с ВЫРАЗИТЬ.

В коде

  • ⚡ не делайте серверный вызов на каждую строку. Передайте таблицу на сервер одним вызовом и посчитайте там;
  • ⚡ уберите строковые преобразования из цикла. Формат() и Строка() создают новые строки и заметно дороже арифметики — форматируйте только то, что выводите;
  • ⚡ не пересчитывайте одно и то же. Если Округл(Сумма, 2) нужен в трёх местах, сохраните в переменную;
  • ⚡ меряйте, а не гадайте. Замер через ТекущаяУниверсальнаяДатаВМиллисекундах() до и после цикла покажет, стоит ли оптимизация усилий.
Начало = ТекущаяУниверсальнаяДатаВМиллисекундах();

Для Каждого Строка Из ТаблицаТоваров Цикл
    Строка.ДробнаяЧасть = Строка.Количество - Цел(Строка.Количество);
КонецЦикла;

Длительность = ТекущаяУниверсальнаяДатаВМиллисекундах() - Начало;
Проверьте себя
Что вернёт Цел(-2.7)?
Чему равно 12.75 % 1?
Что вернёт Округл(2.5) с режимом по умолчанию?

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

Есть ли в 1С отдельная функция для дробной части?
Нет. Дробную часть получают выражением Число - Цел(Число) или Число % 1, результат одинаковый.
Как узнать количество знаков после запятой?
Умножайте дробную часть на 10 в цикле, пока она не станет целой, и считайте шаги. Ограничьте цикл, например, 20 итерациями: у результата деления хвост может быть очень длинным.
Можно ли получить дробную часть в выражении СКД?
Да, в выражениях вычисляемых полей СКД доступны функции Цел() и Окр(), поэтому работает то же вычитание. Для больших объёмов надёжнее посчитать поле прямо в запросе набора данных.
Как округлить до десятков или сотен рублей?
Укажите отрицательную разрядность: Округл(1234.56, -2) вернёт 1200, Округл(1250, -2) — 1300.
Почему дробная часть получилась отрицательной?
Цел() отбрасывает дробь в сторону нуля, поэтому у отрицательного числа дробная часть тоже отрицательная. Нужна неотрицательная — считайте целую часть с округлением вниз.