Почему при делении и умножении одинаковых чисел в 1С появляется погрешность и как это исправить?

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

При отладке кода или выполнении математических вычислений во встроенном языке 1С разработчики иногда сталкиваются со странным мнемоническим эффектом: при последовательном делении и умножении на одно и то же число результат получается неточным. Например, выражение 416.48 / 3.5 * 3.5 может вернуть значение 416.4800000000000000000000000001.

Рассмотрим подробно причины возникновения этой проблемы и разберем практические способы её решения.

Причина возникновения погрешности в вычислениях

Посмотрим на порядок выполнения операций слева направо. При выполнении первого действия 416.48 / 3.5 получается бесконечная или имеющая длинный хвост дробь 118.994285714285714....

Платформа «1С:Предприятие» использует десятичную арифметику с фиксированной точкой (тип Decimal), где разрядность ограниченна внутренним регистром хранения чисел (до 38 знаков). При выполнении деления платформа обрезает и округляет промежуточный результат до доступной точности. Когда затем полученное промежуточное значение умножается на 3.5, возникает накопленная погрешность на 28–30 знаке после запятой, что и отображается в отладчике.

Способ 1. Изменение порядка математических действий

Первый простой вариант решения — сначала выполнить умножение, а затем деление. В этом случае промежуточный результат умножения не теряет точности при усечении дробной части.

Проанализируем пример:


// Приведет к погрешности:
Результат1 = 416.48 / 3.5 * 3.5; // 416.4800000000000000000000000001

// Вернет точное значение:
Результат2 = 416.48 * 3.5 / 3.5; // 416.48

Важно: При работе с очень большими числами выполняя сначала умножение, следует помнить о риске переполнения разрядной сетки, однако для стандартных сумм и количеств в учетных системах этот метод работает отлично.

Способ 2. Группировка делителей с помощью скобок

Если математическое выражение представляет собой последовательное деление или расчет коэффициентов, мы можем изменить приоритет операций с помощью круглых скобок.

Посмотрим на пример из реальной практики вычисления цены или количества:


// Вместо конструкции с последовательным делением и умножением:
Результат = СуммаВДокументе / КоличествоВДокументе * КоличествоВМашине;

// Используем скобки для вычисления коэффициента:
Результат = СуммаВДокументе / (КоличествоВДокументе / КоличествоВМашине);

В этом случае деление (3.5 / 3.5) даст точную единицу, и итоговое число не потеряет точности.

Способ 3. Принудительное округление промежуточных и итоговых значений

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

Синтаксис функции:

Окр(Число, Разрядность, [РежимОкругления])

Разберем пример правильного оформления вычислений:

ТочныйРезультат = Окр(416.48 / 3.5 * 3.5, 2, РежимОкругления.Окр15как20);

Параметр РежимОкругления.Окр15как20 обеспечивает стандартное коммерческое округление, при котором число 0.5 округляется в большую сторону по модулю.

Округление в запросах и при выводе пользователю

Если подобные вычисления выполняются в языке запросов 1С, рекомендуется явно приводить тип результата с помощью функции ВЫРАЗИТЬ. Это исключает различия в работе математических библиотек разных СУБД (MS SQL Server, PostgreSQL и др.):


ВЫБРАТЬ
	ВЫРАЗИТЬ(Таблица.Сумма / Таблица.Количество * Таблица.Коэффициент КАК ЧИСЛО(15, 2)) КАК ИтоговаяСумма
ИЗ
	Документ.Продажи КАК Таблица

Для отображения чисел в печатных формах и интерфейсных отчетах без технических хвостов используйте форматную строку Формат(Число, "ЧДЦ=2"), где параметр ЧДЦ задает количество отображаемых знаков дробной части.

← На главную