Разработка 7 мин чтения

Деньги в программе: почему суммы хранят в копейках

Почему программа хранит рубли целым числом копеек, где копейки пропадают при делении, налоге, скидке и проценте партнёра, какое правило округления записать в ТЗ и как автотестом проверить, что сумма строк равна итогу. Короткая техническая врезка по документации MDN и два примера из моей практики.

деньги в программеокруглениесметырасчётыавтотесты2026

Коротко (TL;DR)

  • Сумму в программе хранят целым числом копеек: 1 999,90 ₽ лежит как 199990. Целые числа складываются без остатка, а дробные в двоичной записи многие копейки хранят приблизительно.
  • Копейки теряются в четырёх местах: при делении, при расчёте налога, скидки и процента партнёра, и при расхождении суммы по строкам с итогом.
  • Правило округления выбирают один раз на документ и записывают в ТЗ: считать по строкам или от итога, куда класть остаток.
  • Главная проверка: сумма строк равна итогу, и это автотест, а не взгляд глазами.
Хотите внедрить это у себя?

Сделаю под ключ: разберу задачу, назову срок и цену. Чтобы начать, напишите в Telegram одной фразой.

Автоматизация бизнеса

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

Зачем хранить деньги в копейках

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

Решение простое. Внутри программы цена 19,90 ₽ хранится как 1990, итог в 59,70 ₽ как 5970. Рубли и запятую программа рисует только на экране и в PDF. Всё, что считается внутри, идёт на целых числах: складывается, вычитается, умножается на количество.

Короткая техническая врезка. В JavaScript число это 64-битное значение формата IEEE 754 двойной точности, это написано в документации MDN по Number. Под дробную часть отведено 52 бита, точность около 15-17 значащих цифр, и арифметика за этим пределом подвержена округлению. Целые без потери точности помещаются до 2 в 53 степени минус 1, это 9007199254740991. В копейках такое число равно примерно 90 триллионам рублей, так что для сметы и счёта запаса хватает с огромным избытком. Другие языки и базы данных устроены по-разному, но принцип тот же: целое число копеек надёжнее дроби.

В консоли Node.js у меня сумма 0,1 и 0,2 даёт 0.30000000000000004, а десять раз по 0,1 в сумме дают 0.9999999999999999. Цена 4,35 ₽, умноженная на 100, выдаёт 434.99999999999994 вместо 435. Это обычное поведение двоичной дроби, и на странице MDN про toFixed есть пример, где 2,55 после округления до одного знака даёт 2,5, потому что ближайшее представимое число чуть меньше.

Где теряются копейки

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

Деление

100 ₽ на троих это 33,33 ₽ каждому, а три раза по 33,33 дают 99,99. Одна копейка осталась ничьей. В целых числах то же самое: 10000 разделить на 3 даёт 3333 и остаток 1.

Налог

Возьмём три позиции по 1,99 ₽ и налог 20% сверху. Налог с каждой строки 0,398, после округления 0,40, три строки дают 1,20 ₽. А налог с итога 5,97 ₽ это 1,194, после округления 1,19 ₽. Те же данные, разница в копейку, в зависимости от того, где округляли.

Скидка и процент партнёра

Скидка 7% на три строки по 19,90 ₽: с каждой строки 1,39 ₽, всего 4,17 ₽. Со всего заказа в 59,70 ₽ скидка получается 4,18 ₽. Процент партнёрской программы устроен так же: 5% с трёх оплат по 99,90 ₽ даёт по 5,00 ₽ с каждой, то есть 15,00 ₽, а с общей суммы 299,70 ₽ выходит 14,99 ₽. Партнёр заметит эту копейку быстрее, чем вы думаете, особенно когда выплат тысячи.

Строки и итог

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

Правила округления в сметах и счетах

Универсального правила нет, есть выбор, который нужно сделать один раз. Я предлагаю заказчику решить три вопроса.

  • Где округляем. Каждую строку отдельно или только итог. Для сметы и счёта, где клиент видит строки, надёжнее строки: итог тогда равен сумме того, что на бумаге.
  • Как округляем половину копейки. Вверх, к чётному или другим способом. В JavaScript функция Math.round округляет половину в сторону плюс бесконечности: по документации MDN, 20,5 даёт 21, а минус 20,5 даёт минус 20. Для отрицательных сумм, например возвратов, это поведение легко не заметить, поэтому правило стоит записать и проверить тестом.
  • Куда девать остаток при делении. Последней строке, самой крупной или по очереди тем, у кого остаток дробной части больше. Главное, чтобы сумма долей была равна целому.

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

Проверка «сумма строк равна итогу»

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

// проценты: сначала умножаем, в конце делим и округляем
function percent(kopeyki, p) {
  return Math.floor((kopeyki * p * 2 + 100) / 200);
}
// делим сумму на n частей, остаток раздаём по копейке
function split(total, n) {
  const base = Math.floor(total / n);
  const rest = total - base * n;
  return Array.from({ length: n }, (_, i) => base + (i < rest ? 1 : 0));
}
// проверка: сумма частей равна целому
console.log(split(10000, 3), split(10000, 3).reduce((a, b) => a + b));
// [3334, 3333, 3333] 10000

Функция percent округляет половину копейки вверх на целых числах, без единой дроби. Если для денег нужно другое правило, меняется одна строка, и тест остаётся тем же.

Второй вид проверки это контрольные суммы из ТЗ: заказчик знает, что в таком-то наборе данных итог должен быть таким-то, и код обязан выдать это число. Так я сделал в проекте с шестью пультами собственника. Карточки там не хранят готовых чисел, каждая цифра собирается из строк набора на каждый запрос, деньги лежат в целых копейках, а контрольные суммы всех семи техзаданий сверяются в тестах. Пока хоть одна строка не сходится, выкладывать нечего.

Как я это делаю

Методика у меня одна для любого проекта, где есть цена, скидка или счёт.

  1. В базе и в коде деньги это целые копейки. Рубли с запятой появляются только на экране, в PDF и в выгрузке.
  2. Количество, которое бывает дробным, хранится отдельно, с оговорённой точностью. В презентации для ремонтной компании я заложил объём до трёх знаков, а деньги до копеек: см. разбор бота для заявки, замера и сметы. Это предложение: бот не запускался, но правило расчёта в нём прописано.
  3. Строка сметы равна количеству, умноженному на цену, округление один раз. Итог равен сумме строк, а не пересчитывается отдельно. Про версии сметы и согласование с клиентом я писал в статьях про версии сметы и про расчёт сметы в боте-квизе.
  4. Правило округления записано в ТЗ в одном абзаце: где, как, куда остаток. На это правило ссылается тест.
  5. Автотест сверяет сумму строк и итог на случайных данных и на примерах заказчика, включая нули, возвраты и минимальные суммы.

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

Сколько это стоит

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

  • Технический аудит расчётов и формул от 50 000 ₽, от 1 недели.
  • Автоматизация процессов и документов (сметы, счета, акты) от 60 000 ₽, от 2 недель.
  • Бот для бизнеса с оплатой, записью и CRM от 80 000 ₽, от 2 недель.
  • Мелкая доработка расчёта в готовой системе: разработка по часам от 3 000 ₽/час.

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

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

Можно ли хранить деньги в базе как десятичное число?

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

Что делать, если уже есть документы с разошедшимися копейками?

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

Зачем отдельный тест, если я проверяю итог глазами?

Глазами проверяются единицы документов, а расхождение живёт в редких сочетаниях сумм. Тест прогоняет тысячи случаев за секунды и ловит их до клиента.

А как быть с валютой и курсом?

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

Коротко о главном

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

Если у вас в смете, счёте или партнёрских выплатах копейки не сходятся, напишите мне в Telegram с кодовым словом «КОПЕЙКА»: посмотрю, где расходится, и скажу, сколько займёт исправление.

Обсудим вашу задачу

Расчёты без расхождений: сметы, счета, партнёрские выплаты

Напишите одной фразой, что нужно. Отвечу, что подойдёт, сколько займёт и сколько стоит. Сообщение уже подготовлено.

  • Деньги в целых копейках и одно правило округления
  • Автотест: сумма строк равна итогу
  • Сметы, счета и акты по вашим формулам
  • Аудит расчётов в таблице или программе
Пишу о разработке, ИИ и законах для бизнеса

Разборы, кейсы и практика — без воды. Выходит регулярно, читать 3–5 минут.

Готовые решения под ключTurnkey solutionsSoluciones llave en mano交钥匙解决方案Түлхүүр гардуулах шийдэл 451 готовых IT-решений для бизнеса451 ready-made IT solutions for business451 soluciones IT listas para empresas451 个面向企业的现成 IT 解决方案Бизнест зориулсан 451 бэлэн IT шийдэл Автоматизация, боты, AI, 152-ФЗ и платформы · бесплатная консультацияAutomation, bots, AI, data privacy and platforms · free consultationAutomatización, bots, IA, privacidad de datos y plataformas · consulta gratis自动化、机器人、AI、数据合规与平台 · 免费咨询Автоматжуулалт, бот, AI, өгөгдлийн хамгаалалт ба платформ · үнэгүй зөвлөгөө Смотреть каталогView catalogVer catálogo查看目录Каталог үзэх