Как получить цифру из числа по ее порядковому номеру без использования строк и округлений в 1С

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В практике разработки на платформе 1С иногда возникают олимпиадные или алгоритмические задачи, в которых требуется разобрать число на разряды в условиях жестких системных ограничений. Рассмотрим задачу: на вход поступает целое число X (значение до пяти разрядов, где каждая цифра равна 0 или 1) и порядковый номер разряда Y (от 1 до 5, нумерация слева направо, то есть от старшего разряда к младшему). Если число содержит меньше пяти разрядов, недостающие старшие разряды считаются равными нулю — например, для числа 1001 первая цифра равна 0.

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

Особенности выполнения арифметических операций во встроенном языке 1С

Прежде чем перейти к коду, проанализируем доступный инструментарий платформы:

  1. Во встроенном языке 1С оператор деления / всегда возвращает точный дробный результат, а отдельного оператора целочисленного деления нет.
  2. Однако операция получения остатка от деления % поддерживается в качестве стандартной арифметической операции над числами.
  3. Вычитание остатка перед делением позволяет моделировать целочисленное деление строго арифметически: выражение (X - (X % 10)) / 10 всегда дает точный целый результат без дробного остатка и без вызова системной функции Цел().
  4. В 1С логическое условие может быть неявно или явно приведено к числу: выражение 0 + (X > 9999) возвращает 1 при истинности условия и 0 при ложности.

Способ 1. Последовательное отсечение младших разрядов в цикле

Рассмотрим первый подход, основанный на вычислении остатка от деления на 10. Так как нумерация разрядов идет слева направо (1 — десятки тысяч, 5 — единицы), то цифра с номером Y находится на позиции 5 - Y справа. Если мы сдвинем число вправо на нужное количество шагов, искомая цифра окажется в разряде единиц, откуда ее легко забрать через оператор остатка от деления % 10.

Посмотрим на пример реализации алгоритма:


&НаКлиенте
Функция F_СдвигВправо(Знач Х, Знач У)
    Шагов = 5 - У;
    
    Для Счетчик = 0 По Шагов Цикл
        Остаток = Х % 10;
        Х = (Х - Остаток) / 10;
    КонецЦикла;
    
    Возврат Остаток;
КонецФункции

Разберем логику работы этого метода:

  1. Определяем, сколько разрядов справа необходимо пропустить до нужной позиции: 5 - У.
  2. На каждой итерации берем текущую последнюю цифру через Х % 10.
  3. Вычитаем полученный остаток из числа и делим на 10. Поскольку разность делится без остатка, дробная часть гарантированно не образуется, что исключает необходимость вызова функции Цел().
  4. После завершения цикла в переменной Остаток оказывается искомая цифра.

Способ 2. Вычитание весов разрядов слева направо

Рассмотрим альтернативное решение, в котором число обрабатывается от старших разрядов к младшим. Поскольку разряды могут принимать только значения 0 или 1, мы можем проверить наличие единицы в текущем разряде путем простого сравнения или вычитания веса этого разряда (10000, 1000, 100, 10, 1).

Так как стандартный оператор возведения в степень (вроде ^ или **) во встроенном языке не поддерживается, вес разряда вычисляется вложенным циклом умножения на 10:


Функция F_ПоразрядноеВычитание(Знач Х, Знач У)
    Перем Цифра;
    
    Для НЦ = 1 По У Цикл
        Множитель = 1;
        Для Степень = 1 По 5 - НЦ Цикл
            Множитель = Множитель * 10;
        КонецЦикла;
        
        Х = Х - Множитель;
        Если Х >= 0 Тогда
            Цифра = 1;
        Иначе
            Цифра = 0;
            Х = Х + Множитель;
        КонецЕсли;
    КонецЦикла;
    
    Возврат Цифра;
КонецФункции

Проанализируем этот алгоритм:

  1. Цикл выполняется ровно У раз — от первого (старшего) разряда до целевого.
  2. Для каждого шага формируется вес текущего разряда Множитель (например, для первой итерации при пятизначном числе это 10000).
  3. Мы пытаемся вычесть данный множитель из числа. Если результат неотрицателен, значит, в текущем разряде находилась единица. Если получен минус, единицы в разряде не было (там был 0), и мы возвращаем вычтенное значение обратно к числу Х.
  4. На шаге У цикл завершается, и переменная Цифра содержит корректный ответ.

Способ 3. Рекурсивный сдвиг влево с тернарным оператором

Выясним, как решить задачу максимально компактно без явных циклов. Для этого используем рекурсию и сдвиг числа влево (умножение на 10). На каждом шаге рекурсии мы проверяем, превышает ли число порог в 9999 (что означает наличие единицы в старшем разряде), после чего «срезаем» старшую единицу и сдвигаем число влево, уменьшая счетчик У.

Посмотрим на пример рекурсивной функции:


Функция F_Рекурсия(Х, У)
    Возврат ?(У < 2, 0 + (Х > 9999), F_Рекурсия((Х - 10000 * (Х > 9999)) * 10, У - 1));
КонецФункции

Разберем по шагам, как работает данная конструкция:

  1. Базовый случай рекурсии: когда У < 2 (то есть мы дошли до искомого разряда), проверяется условие Х > 9999. Выражение 0 + (Х > 9999) преобразует булево значение в число 1 или 0.
  2. Шаг рекурсии: если текущий разряд еще не целевой, вычитаем из числа 10000 (если там была единица: 10000 * (Х > 9999)) и умножаем остаток на 10, продвигая следующий разряд на позицию десятков тысяч.
  3. Параметр У уменьшается на единицу, приближая завершение рекурсии.

Способ 4. Математическая формула через двойной остаток

Если задача допускает прямое вычисление без перебора, можно применить формулу позиционной системы счисления. Чтобы изолировать цифру разряда $10^k$ (где $k = 5 - Y$), берется остаток от деления на $10^{k+1}$ для отсечения старших разрядов, затем вычитается остаток от деления на $10^k$ для обнуления младших разрядов, после чего результат делится на $10^k$:

Цифра = ((X % 10^(k + 1)) - (X % 10^k)) / 10^k

Так как вычитаемое кратно $10^k$, деление всегда выполняется нацело, что строго удовлетворяет условию отсутствия функций округления.

← На главную