Довольно часто при разработке алгоритмов, сравнении показателей или вычислении отклонений мы сталкиваемся с необходимостью получить абсолютное значение числа, или, проще говоря, «модуль». В большинстве языков программирования для этого существует стандартная функция Abs(). Давайте проанализируем ситуацию в 1С:Предприятие и разберем по шагам, как правильно реализовать получение модуля, чтобы код работал быстро и без ошибок.
В 1С:Предприятие (как во встроенном языке, так и в языке запросов) исторически отсутствует встроенная системная функция для прямого получения абсолютного значения числа. Даже в старших версиях платформы 8.3, где разработчики существенно расширили набор математических функций в запросах (добавили тригонометрию, логарифмы, корни и возведение в степень), функция для вычисления модуля так и не появилась. Поэтому программистам приходится реализовывать её самостоятельно. Рассмотрим подробнее самые популярные и эффективные способы.
Самым производительным и надежным способом во встроенном языке является использование тернарного оператора ?(). Этот вариант отрабатывает быстрее остальных, так как вычисление простого логического условия не требует от платформы накладных расходов на вызов отдельных функций и обработку передаваемых параметров.
Посмотрим на пример функции, которую можно вынести в общий модуль:
Функция МодульЧисла(Значение)
Возврат ?(Значение < 0, -Значение, Значение);
КонецФункции
Логика работы предельно проста: если число меньше нуля, мы меняем его знак на противоположный (добавляем минус), в противном случае — возвращаем исходное значение.
Второй популярный, лаконичный и вполне рабочий способ — использование встроенной функции Макс(). Мы просто передаем в нее само число и его отрицательный вариант.
Функция МодульЧисла(Значение)
Возврат Макс(Значение, -Значение);
КонецФункции
Функция вернет наибольшее из двух значений, что математически всегда будет соответствовать модулю числа. Этот код выглядит аккуратно и отлично подходит для использования непосредственно в теле процедур.
Если с встроенным языком всё достаточно просто, то в языке запросов начинающих разработчиков часто подстерегает ошибка. Применить функцию МАКС() для сравнения числа и его отрицательного значения внутри запроса невозможно.
Выясним причину: в синтаксисе запросов МАКСИМУМ — это агрегатная функция. Она предназначена для работы с множеством строк при группировке данных и не умеет сравнивать два произвольных выражения в рамках одной текущей строки.
Поэтому единственный корректный способ получить модуль при выборке из базы данных — это использование конструкции ВЫБОР КОГДА ... ТОГДА ... ИНАЧЕ ... КОНЕЦ. Посмотрим на пример кода в запросе:
ВЫБРАТЬ
ТаблицаДокумента.СуммаОтклонения КАК СуммаОтклонения,
ВЫБОР
КОГДА ТаблицаДокумента.СуммаОтклонения < 0
ТОГДА -ТаблицаДокумента.СуммаОтклонения
ИНАЧЕ ТаблицаДокумента.СуммаОтклонения
КОНЕЦ КАК МодульОтклонения
ИЗ
Документ.НашДокумент.ТабличнаяЧасть КАК ТаблицаДокумента
Если модуль нужен вам исключительно для визуального представления в отчете или для расчета средних/итоговых отклонений, вычисление настоятельно рекомендуется переносить из текста запроса в «Вычисляемые поля» Системы Компоновки Данных (СКД). В выражении вычисляемого поля вы можете использовать тернарный оператор или функцию Макс(). Это делает сам запрос более читаемым и переносит математические расчеты на уровень обработки результата компоновки.
В ходе обсуждений на форумах разработчики иногда предлагают весьма экстравагантные решения. Рассмотрим их, чтобы понять, почему их нельзя использовать в реальных проектах.
Использование возведения в квадрат и последующего извлечения корня:
Функция МодульЧисла(а)
Возврат Sqrt(Pow(а, 2));
КонецФункции
Это неэффективно с точки зрения потребления ресурсов процессора и является плохой практикой.
Варианты с переводом числа в строку, поиском минуса и его удалением (через Найти или СтрЗаменить) таят в себе скрытую угрозу.
// ТАК ДЕЛАТЬ НЕЛЬЗЯ!
Функция МодульЧисла(а)
Возврат Число(СтрЗаменить(Строка(а), "-", ""));
КонецФункции
Проанализируем ситуацию: при неявном преобразовании многозначного числа (например, -1000) в строку платформа 1С автоматически применяет региональные установки и добавляет неразрывные пробелы в качестве разделителей разрядов. Строка примет вид -1 000. Если мы удалим минус и попытаемся вернуть строку обратно через функцию Число(), система может выдать ошибку преобразования типа или вернуть некорректный результат из-за наличия пробелов. Чтобы этот код работал безопасно, потребуется использовать функцию Формат() для отключения группировки разрядов, что сделает алгоритм избыточно громоздким и крайне медленным.
Подведем итоги: используйте конструкцию ? (Значение < 0, -Значение, Значение) для программного кода и ВЫБОР КОГДА ... КОНЕЦ для запросов к базе данных. Это золотой стандарт 1С-разработки, гарантирующий максимальную скорость работы и отсутствие неожиданных ошибок преобразования типов.