1C-CodeОсновы 1С → Числа: арифметика, округление и деньги

Числа: арифметика, округление и деньги

Тип, с которого начинается любая учётная задача. Подробно про округление — там теряют копейки.

Глава 5 из 36 · чтение примерно 24 минут

Число — самый частый тип в 1С. Суммы, количества, проценты, номера, дни просрочки — всё это числа, и почти каждая задача сводится к тому, чтобы посчитать их правильно.

Правильно — значит до копейки. Именно поэтому половина главы про округление: там теряют деньги, и теряют незаметно.

Целые и дробные — один тип

В 1С нет отдельно «целого» и «дробного» числа. Тип один — Число, и в него одинаково хорошо кладётся 5 и 5,25.

В коде дробная часть отделяется точкой, а не запятой: Цена = 10.5;. На экране 1С покажет запятую, как принято в русском языке, но пишут в коде точку.

Число в 1С — десятичное, и это редкость

Почти во всех языках дробные числа хранятся приближённо, в двоичном виде. Знаменитый пример: 0.1 + 0.2 там не равно 0.3, а даёт 0,30000000000000004.

В 1С этой проблемы нет. Число здесь десятичное с фиксированной точностью, и 0,1 + 0,2 равно ровно 0,3. Для учётной системы это принципиально: копейки не должны появляться и исчезать сами.

Если вы читали про «ошибки плавающей точки» в других языках — здесь этот приём не нужен. А вот про округление — нужен, и очень.

Четыре действия

Сумма = 250 + 100;      // 350
Остаток = 250 - 100;    // 150
Итого = 250 * 4;        // 1000
Средняя = 1000 / 4;     // 250
ЧастьЧто означает
+ − * /как в математике, порядок действий тоже обычный
скобкименяют порядок: (2 + 3) * 4 = 20, а 2 + 3 * 4 = 14
делениевсегда даёт дробное: 7 / 2 = 3,5, а не 3
деление на нольне даёт бесконечность — программа прекращает работу с ошибкой

Упражнение 1. Сумма с наценкой

Напишите функцию СЦеной(Закупочная, Наценка), которая возвращает цену продажи: закупочная плюс наценка в процентах. Для 100 и 30 должно получиться 130.

Решается в тренажёре: код запускается и проверяется сразу.

Цел и Окр — разные вещи

Две команды, которые путают чаще всего.

Цел(Значение)отбрасывает дробную часть. Не округляет, а именно отрезает. Цел(7.9) даёт 7, а не 8.

Окр(Значение, Разрядов) — округляет по правилам. Окр(7.9) даёт 8.

Чем отличаются

ВыражениеРезультатЧто произошло
Цел(7.9)7дробная часть отброшена
Окр(7.9)8округлено до ближайшего
Цел(-7.9)−7отброшена дробь — значение приблизилось к нулю
Окр(-7.9)−8округлено до ближайшего — значение ушло от нуля
Окр(1.2345, 2)1,23два знака после запятой
Окр(1250, -2)1300отрицательный разряд округляет до сотен

Отрицательные числа: Цел коварнее, чем кажется

Цел(-7.9) — это −7, а не −8. Отбрасывание дробной части всегда двигает число к нулю, в какую бы сторону вы ни думали.

На положительных числах это совпадает с «округлением вниз», и потому ошибку не замечают — пока в данных не появится минус. А он появляется: возвраты, корректировки, отрицательные остатки.

Упражнение 2. Сколько целых коробок

В коробку помещается 12 штук. Напишите функцию ЦелыхКоробок(Штук), которая возвращает, сколько коробок заполнится полностью. Для 30 штук это 2 коробки (шесть штук останутся).

Решается в тренажёре: код запускается и проверяется сразу.

Остаток от деления

Знак % даёт остаток: 17 % 5 — это 2, потому что 17 = 5 × 3 + 2.

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

Упражнение 3. Что не влезло

Дополните: функция Остаток(Штук) возвращает, сколько штук останется сверх полных коробок по 12. Для 30 штук — 6.

Решается в тренажёре: код запускается и проверяется сразу.

Округление денег: где теряют копейки

Правило, которое надо запомнить дословно: округлять надо каждую строку, а не итог. Потому что Окр(а) + Окр(б) и Окр(а + б) — разные числа.

✓ Каждую строку

Окр(10.005 * 3, 2) = 30.02
Окр(10.005 * 3, 2) = 30.02
         итого   60.04

Так печатается накладная, и так складывает бухгалтер.

✕ Итог целиком

10.005 * 3 = 30.015
10.005 * 3 = 30.015
Окр(60.03, 2) =  60.03

Копейка разницы — и документ не сходится со своей же печатной формой.

Почему копейка расходится

Расхождение на копейку — самая частая причина жалобы «документ не проводится, а всё правильно». Ошибка не в данных и не в проверке, а в порядке действий: где именно поставили округление.

Найти это трудно, потому что на большинстве документов сумма сходится: разница появляется только когда цена содержит больше двух знаков — после пересчёта из валюты, после расчёта скидки, после деления на количество.

Упражнение 4. Сумма строки как в накладной

Напишите функцию СуммаСтроки(Цена, Количество): произведение, округлённое до копейки. Для цены 10.005 и количества 3 должно получиться 30.02.

Решается в тренажёре: код запускается и проверяется сразу.

Два способа округлить половину

Что делать с ровно половиной — 2,5? Есть два правила, и 1С умеет оба.

Окр(2.5) без третьего параметра даёт 3: половина уходит от нуля. Это привычное «школьное» округление, и в учёте используется оно.

Окр(2.5, 0, 0) даёт 2: половина уходит к чётному. Такое округление называют банковским — при большом числе операций оно не накапливает перекос в одну сторону.

Какое выбирать

По умолчанию — обычное, оно же принято в бухгалтерии и в печатных формах. Банковское применяют, когда об этом прямо сказано в требованиях: например, в расчётах с некоторыми банками или в статистике.

Главное — не смешивать в одной системе. Разные правила в разных местах дают расхождения, которые потом сходятся вручную ночами.

Деление на ноль

Деление на ноль в 1С — не бесконечность и не ноль, а прекращение работы с ошибкой. Обработчик, упавший на одном менеджере из тридцати, не покажет отчёт вообще.

Поэтому знаменатель проверяют до деления, а не после. После — поздно: до проверки дело не дойдёт.

Упражнение 5. Процент выполнения плана

Напишите функцию Процент(Факт, План). Если план не больше нуля — вернуть 0, не деля. Иначе вернуть процент выполнения, округлённый до одного знака.

Решается в тренажёре: код запускается и проверяется сразу.

Распределить сумму без потери копейки

Задача, которая встречается в учётной работе постоянно: разложить сумму документа по строкам, скидку — по товарам, доставку — по местам.

Сделанная в лоб — делим и округляем каждую долю — она даёт расхождение. Сто рублей на троих: 33,33 плюс 33,33 плюс 33,33 равно 99,99. Копейка исчезла, документ не сходится, и бухгалтер её найдёт.

Приём против этого один и применяется везде: последняя доля не считается, а берётся остатком. Тогда сумма долей равна исходной всегда — по построению, а не по счастливому совпадению.

✓ Так — копейка теряется

Для Каждого Строка Из Строки Цикл
	Строка.Доля = Окр(Сумма / Строки.Количество(), 2);
КонецЦикла;
// 33,33 + 33,33 + 33,33 = 99,99

Каждая доля округлена сама по себе. Ошибки округления складываются, и расхождение растёт со строками.

✕ Так — сходится всегда

Остаток = Сумма;
Для Сч = 1 По Всего Цикл
	Если Сч = Всего Тогда
		Доля = Остаток;          // последняя — всё, что осталось
	Иначе
		Доля = Окр(Сумма / Всего, 2);
		Остаток = Остаток - Доля;
	КонецЕсли;
КонецЦикла;
// 33,33 + 33,33 + 33,34 = 100,00

Лишняя копейка честно достаётся последней строке. Сумма долей равна исходной при любом числе строк.

Почему именно последней

Кому достанется копейка — вопрос не математики, а договорённости. В типовых конфигурациях её отдают строке с наибольшей суммой: там она менее заметна в процентах.

Но принцип от этого не меняется: одна из долей считается остатком, а не формулой. Кто именно — решают уже по задаче.

И ещё: распределять надо пропорционально, если строки разного веса. Делить поровну можно только когда они действительно равны — например, места в доставке.

Упражнение 7. Разложить сумму по частям

Напишите Распределить(Сумма, Частей): верните доли через / , каждая с двумя знаками. Последняя доля — остаток, чтобы сумма сошлась. Для 100 на 3 ожидается 33,33 / 33,33 / 33,34.

Решается в тренажёре: код запускается и проверяется сразу.

НДС: выделить и начислить

Две операции, которые путают, и обе встречаются в первой же учётной задаче.

Начислить НДС сверху: Сумма * СтавкаНДС / 100. Было 1000 без НДС, стало 1200 с НДС при ставке 20%.

Выделить НДС из суммы, в которой он уже сидит: Сумма * СтавкаНДС / (100 + СтавкаНДС). Из 1000 с НДС выделяется 166,67, а не 200.

Формула «20/120» — это она и есть, записанная числами. Именно её чаще всего и вспоминают неправильно.

Округлять один раз, и не раньше времени

НДС считают от итога строки, а не от цены, и округляют один раз — в конце.

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

Это общее правило, не только про НДС. Любая цепочка вычислений округляется один раз — там, где результат показывают или записывают.

Упражнение 8. Выделить НДС

Напишите НДСИзСуммы(Сумма, Ставка): выделить НДС из суммы, в которой он уже есть, и округлить до копеек. Для 1000 и ставки 20 ожидается 166.67.

Решается в тренажёре: код запускается и проверяется сразу.

Почему 0,1 + 0,2 в 1С даёт ровно 0,3

Если вы программировали на других языках, то знаете неприятность: там 0.1 + 0.2 не равно 0.3, потому что дробные числа хранятся двоичными и десятичную дробь точно не представляют.

В 1С этой проблемы нет. Число здесь десятичное с фиксированной точностью — сделано специально ради денег. Сравнивать суммы обычным = можно, и это правильно.

Знать об этом стоит по двум причинам: чтобы не тащить из других языков лишние ухищрения со сравнением через малую погрешность, и чтобы ответить на собеседовании, чем число в 1С отличается от привычного.

Длина и точность в базе

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

Важно, что это не подсказка, а ограничение. Число, не помещающееся в разрядность, обрезается при записи: посчитали 1234,567 при точности 2 — в базу легло 1234,56, и дальше все отчёты будут считать по нему.

Второе: длина считается ВСЕГО, вместе с дробной частью. Длина 5 и точность 2 — это максимум 999,99, а не 99999,99. На этом спотыкаются, заводя реквизит для процентов.

Полезные мелочи

Команды, которые пригодятся сразу

КомандаЧто делаетПример
Макс(а, б)большее из двухМакс(Остаток, 0) — «не меньше нуля»
Мин(а, б)меньшее из двухМин(Пеня, Долг) — «не больше долга»
Цел(x)отбросить дробную частьЦел(30 / 12) = 2
Окр(x, n)округлить до n знаковОкр(1.2345, 2) = 1,23
Число("250")превратить строку в числопри загрузке из файла
Строка(250)превратить число в строкучтобы приклеить к тексту
x % yостаток от деления17 % 5 = 2

Макс с нулём вместо Если

Приём, который стоит завести в привычку: Макс(Значение, 0) означает «значение, но не меньше нуля».

Это короче четырёх строк с Если и закрывает целый класс мест, где отрицательное число выглядит как поломка: остаток, нехватка, дни просрочки, размер скидки.

Упражнение 6. Не меньше нуля

Напишите функцию НеХватает(Сумма, Минимум): сколько добрать до минимальной суммы заказа. Если сумма уже достаточна — вернуть 0, а не отрицательное число. Обойдитесь без Если.

Решается в тренажёре: код запускается и проверяется сразу.

Что дальше

Числа освоены. Следующая глава — строки: как их склеивать, резать, искать в них и превращать в числа и обратно. Там своя группа ловушек, в основном связанных с тем, что строку легко спутать с числом.

Как закрепить

Читать про код и писать код — разные умения. В тренажёре к этой главе идут упражнения: вы пишете решение, оно запускается на настоящем интерпретаторе 1С и проверяется сразу, без установки платформы.

Открыть эту главу в тренажёре

Где живёт код: модули, клиент и сервер
Строки: склеить, разрезать, найти, проверить