Почему кастомный калькулятор в мобильном приложении врёт
Кастомный калькулятор в мобильном приложении почти никогда не врёт намеренно — он ошибается на мелочах, которые накапливаются. Пользователь складывает три позиции, пересчитывает вручную и не сходится. Расхождение может быть всего в последнем знаке, но дальше неважно, кто прав: доверие к расчёту потеряно, а поддержка получает спор, который нечем закрыть.
Под капотом обычно двоичная арифметика с плавающей точкой, где 0,1 и 0,2 в сумме дают не ровно 0,3. На одной операции это незаметно, а на длинной смете из десятков строк погрешность округления накапливается и всплывает в итоге — причём по-разному в зависимости от порядка слагаемых.
Вторая причина — разные формулы на разных экранах. Калькулятор в карточке, пересчёт в корзине и выгрузка документа живут на своих реализациях: где-то скидка считается до налога, где-то после, где-то округление идёт вверх. Один и тот же заказ даёт три разных итога, и пользователь ловит приложение на этом первым.
Третий слой — ручной ввод и разбор формул. Точка или запятая как разделитель, пробелы между разрядами, отрицательные значения, вставка числа из буфера, пользовательское выражение со скобками: каждый необработанный случай даёт молчаливо неверный результат вместо ошибки ввода.
Разбираем, как это закрывается единым расчётным слоем: десятичная арифметика вместо плавающей, одно место для правил округления, общие формулы для всех экранов и покрытые проверками сценарии разбора — чтобы итог совпадал и на экране, и в выгрузке.
Что даёт вычислительное ядро, собранное под задачу
Арифметика в расчётном приложении ломается не на сложении, а на краях: округлении налогов, пересчёте при смене единиц измерения, длинных цепочках операций с промежуточными сохранениями. Если формулы продублированы в интерфейсе и в фоне, ветки начинают расходиться в младшем разряде — и это самая дорогая категория дефектов, потому что воспроизводится не у каждого пользователя.
Вычислительное ядро решает это архитектурно: единый источник формул, который вызывают все экраны и фоновые задачи. Мы собираем его как отдельный модуль на Kotlin Multiplatform и подключаем к обеим платформам — правка коэффициента или порядка операций делается в одном месте и не требует двух независимых релизов. Внутри — десятичная арифметика с фиксированной точностью, явные правила округления на каждом шаге и разбор выражения в дерево: порядок операций задан структурой, а не порядком вызовов в коде экрана.
Скорость пересчёта на длинных сценариях держится на инкрементальном обходе дерева и кэше промежуточных узлов. Когда пользователь меняет одно поле в цепочке из десятков связанных значений, пересчитывается только зависящая ветка, остальное берётся из кэша — таблица обновляется без заметной задержки, а профилирование идёт на реальных сценариях, а не на синтетике.
Дальше эффект считается в поддержке. Единый источник формул убирает расхождения между платформами, поэтому доля спорных расчётов падает, а обращения смещаются с «у меня не сходится» на вопросы про использование. Нагрузка на поддержку перестаёт зависеть от числа релизов: контрольные наборы значений в автотестах фиксируют результат, и любое отклонение ловится до выкладки.
Способы реализации расчётной логики в продукте
Расчётная логика может жить на устройстве, на общей стороне или в гибридной схеме — этот выбор определяет поведение продукта при обрыве сети, скорость правки формул и то, где оседает история операций. Ниже — реальные варианты, между которыми выбирают, вместе с их слабыми местами.
| Вариант | Когда подходит | Ограничения |
|---|---|---|
| Расчёт на устройстве | Формулы стабильны, нужен мгновенный отклик и полная работа без сети | Правку формулы нельзя выкатить без новой версии приложения; поведение на разных платформах приходится сверять отдельно |
| Расчёт на общей стороне | Правила меняются часто, нужен единый результат по всем пользователям | Без сети калькулятор не считает; каждый шаг зависит от канала связи; история операций уходит с устройства |
| Гибридная схема | Нужны оба свойства: быстрый отклик и управляемые извне правила | Сложнее сборка, требуется явная механика синхронизации результатов и разрешения конфликтов версий формул |
Как выбираем вариант
Развилка упирается в два вопроса: как часто меняются правила расчёта и обязан ли продукт считать без сети. Если формулы стабильны, а связь у пользователя не гарантирована — считаем на устройстве и обновляем правила вместе с релизом. Если правила правятся постоянно и важен одинаковый ответ для всех — выносим расчёт на общую сторону. Гибрид берём, когда нужны оба свойства: локальный счёт для отклика и работы без сети плюс общие правила и синхронизация результатов, когда связь появляется.
Этапы разработки расчётного модуля
Расчётное ядро ломается не на интерфейсе, а на арифметике: неверное округление на промежуточном шаге даёт расхождение с эталоном, которое замечают уже пользователи. Поэтому порядок работ строим так, чтобы код писался после согласованной схемы, а не параллельно с ней.
-
Разбор формул. Собираем первоисточники: нормативные документы, таблицы, файлы, по которым считают вручную. Для каждой формулы фиксируем входы и выходы, единицы измерения, правила округления, поведение при делении на ноль и выходе за диапазон.
-
Схема расчёта. Строим граф зависимостей: какие величины первичные, какие вычисляются, где допускается промежуточное округление. Схему согласуем письменно — правка на этом этапе стоит минуты, в готовом приложении — переписывания ядра.
-
Реализация. Парсинг ввода (локаль, разделители, экспоненциальная запись) отделяем от вычислительного ядра — оно остаётся чистой функцией без состояния. Точные величины считаем в десятичной арифметике вместо чисел с плавающей точкой.
-
Тестовые наборы. Эталонные примеры от заказчика дополняем граничными случаями: максимумы, отрицательные значения, ноль, повторное применение операции, смена единиц.
-
Проверка результатов. Прогоняем наборы автотестами с допуском по последнему знаку, расхождения разбираем до нуля. Считаем на реальном объёме данных, а не на трёх показательных примерах.
-
Передача модуля. Отдаём документацию по формулам, схему расчёта, тестовые наборы в репозитории и описание правил округления. Обучаем команду и остаёмся на поддержке — модуль развивается без нас.
Что команда заказчика получает по итогам работы
Готовое приложение без документации живёт до первого изменения формулы: расчёт расходится с ожиданием бизнеса, а найти место правки некому. Поэтому по итогам работы передаём не сборку, а полный рабочий комплект — по каждому артефакту видно, как считается результат и кто вправе его менять.
- Исходный код — репозиторий с историей изменений и разделёнными слоями: разбор ввода, вычислительное ядро, отображение. Собирается из чистой ветки одной командой, без ручных правок на машине разработчика.
- Описание формул — таблица соответствия «бизнес-правило → формула → источник требования». Спор о том, какая ставка или коэффициент верны, решается сверкой с этим документом, а не перепиской.
- Схема данных — что считается и хранится на устройстве, что уходит на сервер, как версионируются сохранённые расчёты. Старые записи читаются после смены формата.
- Права доступа — роли сотрудников заказчика: кто меняет коэффициенты, кто утверждает выпуск версии, где лежат ключи подписи и как они отзываются при уходе человека.
- Регламент обновления — как выкатить новую формулу, не выпуская новую версию приложения: набор правил проверяется на эталонных расчётах, откат — возврат предыдущего набора.
- Эталонные расчёты — подборка граничных случаев: нули, отрицательные значения, округление, разделители дробной части, порядок операций. Расхождение ловится до выхода, а не по жалобе клиента.
- Инструкция по окружению и сборке — какие инструменты нужны, как поднять сборочный контур с нуля и как выпустить приложение в магазины от аккаунта заказчика.
Практика: пересчёт смет в строительном сервисе
Что было
Сметы по объектам считали в два прохода: инженер замерял объёмы на площадке и фиксировал их в блокноте, а в офисе переносил те же позиции в 1С и пересчитывал вручную. Второй проход занимал 40–60 минут на объект и фактически был единственной проверкой первого. Расхождения всплывали уже на этапе акта — когда объёмы закупки и наряды бригадам были подписаны.
Причины были системные: разные правила округления на площадке и в офисе, разные формулы для коэффициентов, потерянные при переносе позиции. По нашим замерам 6–9% итогов по объекту не сходились, а обращения в поддержку по арифметике в смете давали около трети всех заявок первой линии.
Что изменили
Формулы вынесли в один модуль: кастомный калькулятор в мобильном приложении, офисное рабочее место и выгрузка в учётную систему считают по одним правилам и с одной точностью. Черновик считается на площадке без связи, при появлении сети уходит на сервер, а автосверка сравнивает итоги построчно и подсвечивает расхождение до отправки — спорные позиции разбирают на объекте, а не в переписке с заказчиком.
После внедрения расчёт по объекту занимает 8–10 минут вместо 40–60, ручной пересчёт как отдельный этап убран. На контрольной выборке объектов расхождений в итогах между черновиком и финальной сметой — ноль, доля обращений в поддержку по расчётам упала ниже 2%.
Чем отличается расчёт на устройстве от расчёта на сервере
Если формула считается прямо на устройстве, результат появляется в момент касания: нет запроса, нет ожидания ответа, нет зависимости от качества канала. В лифте, на складе, в роуминге приложение продолжает считать, а введённые промежуточные значения остаются в локальном хранилище.
Расчёт на сервере даёт один источник формул: справочники и коэффициенты меняются без выпуска новой версии, история расчётов доступна с любого устройства. Плата за это — задержка на каждом шаге и полная неработоспособность без сети: калькулятор превращается в форму, которая ждёт ответа.
Отдельная тема — единообразие. Вещественные числа расходятся в младшем разряде при разном порядке операций, поэтому суммы ведём в целочисленных минорных единицах, а правило округления фиксируем явно и описываем как в IEEE 754. Эталонные наборы входов прогоняем через обе реализации и сверяем результат до последнего знака.
Дороже всего обходится поддержка двух копий логики: два источника истины неизбежно расходятся, и тогда пересчёт итогов задним числом выдаёт другие суммы в уже закрытых документах. Лечится общим модулем расчёта, который собирается из одной кодовой базы под обе платформы, версией формулы внутри каждого сохранённого расчёта и миграцией при её смене.
На практике рабочая схема гибридная: счёт идёт на устройстве, а сервер отвечает за справочники, синхронизацию и пересчёт истории по запросу. Тогда отзывчивость и работа без сети остаются у пользователя, а единообразие формул — под контролем на стороне сервиса.
Технические сомнения перед стартом работы
Перед стартом инженерная команда задаёт одни и те же вопросы: как обеспечивается точность, что происходит при изменении формул, можно ли встроить расчёт в уже работающее приложение и как доказать, что прежние итоги не поехали. Ниже — ответы по существу.
Насколько точным будет расчёт?
Считаем в десятичной арифметике с фиксированной шкалой, а не в двоичной с плавающей точкой: длинные цепочки начислений не накапливают погрешность, а итог сходится до последнего знака. Правило округления задаётся явно — до копейки, половина вверх или по банковской схеме — и закрепляется проверками на граничных суммах, где расхождение проявляется раньше всего.
Что будет, когда формулы изменятся?
Формулы, тарифные сетки и коэффициенты описываем декларативно и выносим в конфигурацию, а не в код. Новое правило или изменённый тариф подхватываются при очередном запуске — пересборка и повторная публикация в магазинах не нужны.
У нас уже есть своя логика расчёта — как быть?
Сохраняем её поведение как эталон: до первой строки нового кода сверяем итоги на ваших примерах, а само ядро расчёта выносим отдельным модулем за стабильным интерфейсом. Обвязку и переход делаем частями, не останавливая текущее приложение.
Как проверить неизменность прежних результатов?
На старте фиксируем набор эталонных сценариев: тарифные срезы, граничные суммы, скидки, валюты. Каждая сборка прогоняет его и сравнивает с сохранённым итогом — расхождение хотя бы в одном знаке блокирует выпуск. Набор передаём вам вместе с документацией, поэтому проверку можно повторять своими силами и после передачи.
Обсудим задачу и вернёмся с оценкой и планом
Чтобы вернуться с оценкой и планом работ, а не с уточняющими вопросами, нам достаточно одного письма с описанием задачи. Чем конкретнее исходные данные по кастомному калькулятору, тем меньше допущений в оценке и тем реальнее сроки.
- Что считает калькулятор: список формул, порядок операций, правила округления и требования к точности на длинных цепочках вычислений.
- Как пользователь вводит данные: пошаговый мастер, свободное выражение с разбором синтаксиса или импорт значений из таблицы.
- Платформы и минимально поддерживаемые версии системы, нужна ли работа без сети и перенос результата между устройствами.
- Что уже есть: макеты, прототип, таблица с формулами или прошлая версия приложения, с которой нужна совместимость.
- Интеграции и справочные данные: курсы, тарифы, внешние сервисы, выгрузка результата в файл или отправка на почту.
- Доступы: аккаунты магазинов приложений, репозиторий, стенды и тестовые данные — их можно передать на этапе старта.
- Ограничения: сроки, в которые нужно уложиться, и платформа, которая в приоритете.
На основе этого мы вернёмся с планом работ по этапам, оценкой сроков и составом команды: кто отвечает за расчётное ядро, кто за интерфейс и сборку. Если каких-то данных не хватает, обозначим это отдельно и предложим, как закрыть до старта — опишите задачу, и мы вернёмся с оценкой и планом.







