Кейс: отчёт по выручке за месяц показывал разные суммы при пересчёте
Финансовый отчёт за месяц суммировал суммы заказов через double: каждая строка заказа добавлялась в аккумулятор, итог показывался в интерфейсе. При повторном запуске отчёта на тех же данных сумма иногда отличалась на несколько копеек.
Причина — double не хранит десятичные дроби точно, а порядок сложения влияет на итоговую погрешность округления. Если суммировать те же числа в другом порядке (например, после изменения сортировки заказов в выборке), результат накопления немного меняется.
double total = 0.0; for (Order order : orders) { total += order.getAmount(); } // сумма зависит от порядка сложения
Исправили переходом на BigDecimal с фиксированным scale для денежных сумм — сложение там не накапливает погрешность округления между шагами.
BigDecimal total = BigDecimal.ZERO; for (Order order : orders) { total = total.add(order.getAmount()); }
Что спросят следом на собесе: почему BigDecimal не полностью решает вопрос точности при делении. Ответ — деление без явного scale и RoundingMode может бросить ArithmeticException на периодической дроби, например 10 разделить на 3. Ещё спросят, почему нельзя было просто округлить double до двух знаков после запятой в конце — потому что погрешность уже накопилась внутри промежуточных сложений, округление в конце её не убирает.
Для денег double не подходит не из-за конкретной ошибки в коде, а из-за самой природы двоичного представления дробей.
Тренажёр: 600 вопросов, мок с таймером, план повторов
senior·base — что спрашивают на самом деле
В этом посте были ссылки, но мы их удалили по правилам Сетки