Методика и точность

Как устроены расчёты Amortiva и почему им можно верить до копейки

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

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

Проверки в цифрах

  • 7 436вызовов арифметики зафиксировано и сверяется с сервером
  • 5 003операций с деньгами зафиксировано и сверяется с сервером
  • 12тестов точности покрывают все расчётные движки

Эти цифры не набраны руками: страница читает их при сборке из тех самых файлов-фикстур и тестов, по которым проверяются движки. Изменилась фикстура — изменится и страница.

Почему калькуляторы теряют копейки

Большинство калькуляторов считает в числах с плавающей запятой. У таких чисел ошибка относительная: на небольших суммах она незаметна, но чем больше сумма, тем крупнее «цена деления» — на очень больших остатках она превышает копейку, и точнее число записать просто нельзя. А привычные 0,1 и 0,01 в двоичном виде вообще бесконечны, поэтому даже простое сложение цен накапливает пыль в младших разрядах.

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

Самый короткий пример: 1 рубль под 1% годовых на полгода. Истинный итог — ровно 1,005 рубля: это «ничья» округления, которая по общему правилу идёт вверх, к 1,01. Любой расчёт с фиксированной точностью приходит чуть ниже — 1,00499… — и отдаёт 1,00, на копейку меньше. Числа с плавающей запятой ошибаются на похожих примерах с другой стороны.

Как считает Amortiva

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

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

Округление выполняется один раз, в самом конце, от истинного значения — до числа знаков конкретной валюты: два у рубля, ноль у иены, три у динара. Правило одно на весь продукт: половина округляется вверх. Двойных округлений — сначала до промежуточной точности, потом до копеек — нет, а именно они дают знакомое «где-то потерялась копейка».

Ставка никогда не делится заранее. Месячное начисление — это остаток * ставка / 1200, вычисленное как одна точная дробь, а не заранее округлённая «месячная ставка», в которой ошибка сидела бы ещё до умножения на остаток.

Почему браузер и сервер дают один результат

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

Поэтому равенство не предполагается, а зафиксировано. Из браузерного движка записаны тысячи реальных вызовов арифметики — аргументы и точный ответ каждого. Эти записи лежат в обоих репозиториях, и серверная реализация обязана воспроизвести каждый ответ в точности; контроль — отпечаток SHA-256 файла, прошитый в проверки обеих сторон. Изменение одной копии без другой роняет сборку ещё до выхода изменения.

Арифметика ставок и долей — 7 436 вызовов, SHA-256: be56be53bfd0a6a6ec5e7fb66a9097ae96d427aad12b84a8cd7f63a0a32212c8

Денежный тип и точные дроби — 5 003 операций, SHA-256: 2f2c42246a0fa42c4550a6bc1997d9e7311037496c803f6b5b58a284a64cda36

Проверено на каждом движке

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

Эта проверка — не формальность. Когда движки переводились на точную арифметику, старую «плавающую» версию прогнали против точного эталона на 3 024 сочетаниях входных данных: она ошиблась в 22 из них, в худшем случае — на 2,93 рубля. Текущий движок на тех же данных не ошибается ни разу.

Если график не совпал с банковским

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

  • База года. Мы начисляем проценты по фактическим дням и фактической длине года — 365 или 366. Часть договоров считает год за 360 или всегда за 365 дней; на високосном году это уже даёт разницу.
  • Перенос платежа. Если дата платежа выпала на выходной, банк списывает деньги в ближайший рабочий день — и за добавочные дни набегают проценты, которых в «календарном» графике нет.
  • Порядок списания. При частичном платеже банк распределяет деньги по правилам договора — комиссии, неустойка, проценты, тело долга — и его внутренние округления на каждом шаге могут отличаться от наших.
  • Дата расчёта. График строится на дату, которую вы указали; выписка банка — на дату фактической операции. День разницы — это день процентов.

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

Результаты носят справочный характер и не являются финансовой рекомендацией.

Мы используем cookie, чтобы сайт помнил ваш вход, тему и сохранённые расчёты. Аналитических и рекламных cookie нет. Подробнее о cookie