BigDecimal для финансовых вычислений
Используйте BigDecimal, чтобы избежать ошибок точности чисел с плавающей точкой при расчёте денежных сумм и налогов.
«BigDecimal для финансовых вычислений» — бесплатный урок Java Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
BigDecimal для финансовых расчётов
Типы с плавающей точкой (double, float) не могут точно представить все десятичные значения из-за двоичного представления. Для денежных сумм и налогов используйте BigDecimal.
Проблема чисел с плавающей точкой
Двоичное представление чисел с плавающей точкой приводит к крошечным ошибкам округления. В финансовых приложениях эти ошибки накапливаются и становятся причиной реальных ошибок в работе программы.
double price = 0.1 + 0.2;
System.out.println(price); // 0.30000000000000004 (WRONG!)
System.out.println(price == 0.3); // false
// Real bug: total never equals expected
double total = 0;
for (int i = 0; i < 10; i++) total += 0.1;
System.out.println(total); // 0.9999999999999999Безопасное создание BigDecimal
Всегда создавайте BigDecimal из String, а не из double. Конструктор с аргументом double наследует неточность представления чисел с плавающей точкой.
import java.math.BigDecimal;
// WRONG: double constructor
BigDecimal wrong = new BigDecimal(0.1);
System.out.println(wrong); // 0.1000000000000000055511...
// CORRECT: String constructor
BigDecimal correct = new BigDecimal("0.1");
System.out.println(correct); // 0.1
// Or use valueOf (also safe)
BigDecimal safe = BigDecimal.valueOf(0.1);
System.out.println(safe); // 0.1Базовая арифметика с BigDecimal
BigDecimal использует вызовы методов для выполнения арифметических операций: add(), subtract(), multiply(), divide(). Эти методы возвращают новые экземпляры BigDecimal, поскольку объекты неизменяемы.
import java.math.BigDecimal;
BigDecimal price = new BigDecimal("19.99");
BigDecimal tax = new BigDecimal("0.08");
BigDecimal qty = new BigDecimal("3");
BigDecimal subtotal = price.multiply(qty);
BigDecimal taxAmount = subtotal.multiply(tax);
BigDecimal total = subtotal.add(taxAmount);
System.out.println(subtotal); // 59.97
System.out.println(taxAmount); // 4.7976
System.out.println(total); // 64.7676Режимы округления
RoundingMode определяет способ округления BigDecimal. Основные режимы:
HALF_UP— стандартное округление (0.5 округляется вверх)HALF_EVEN— банковское округление (0.5 округляется до ближайшего чётного числа)DOWN— усечение к нулюCEILING— округление к положительной бесконечности
import java.math.*;
BigDecimal val = new BigDecimal("2.565");
System.out.println(val.setScale(2, RoundingMode.HALF_UP)); // 2.57
System.out.println(val.setScale(2, RoundingMode.HALF_EVEN)); // 2.56 (banker's)
System.out.println(val.setScale(2, RoundingMode.DOWN)); // 2.56
System.out.println(val.setScale(2, RoundingMode.CEILING)); // 2.57Деление и масштаб
При делении с помощью BigDecimal необходимо указать масштаб и режим округления, чтобы избежать ArithmeticException для бесконечных десятичных дробей.
import java.math.*;
BigDecimal revenue = new BigDecimal("1000.00");
BigDecimal months = new BigDecimal("3");
// Without scale → ArithmeticException (non-terminating decimal)
try {
revenue.divide(months);
} catch (ArithmeticException e) {
System.out.println("Need to specify scale!");
}
// Correct: specify scale and rounding
BigDecimal monthly = revenue.divide(months, 2, RoundingMode.HALF_UP);
System.out.println(monthly); // 333.33Сравнение BigDecimal
Для сравнения значений используйте compareTo(), а не equals(). equals() учитывает масштаб: 2.0 и 2.00 не равны при использовании equals(), но равны при использовании compareTo().
import java.math.BigDecimal;
BigDecimal a = new BigDecimal("2.0");
BigDecimal b = new BigDecimal("2.00");
System.out.println(a.equals(b)); // false (different scale)
System.out.println(a.compareTo(b)); // 0 (same value)
System.out.println(a.compareTo(b) == 0); // true
// For sorting in collections
import java.util.*;
List<BigDecimal> prices = Arrays.asList(
new BigDecimal("3.50"), new BigDecimal("1.00"), new BigDecimal("2.75")
);
Collections.sort(prices);
System.out.println(prices); // [1.00, 2.75, 3.50]MathContext для значащих цифр
MathContext задаёт точность (количество значащих цифр) и режим округления. Используйте его, когда нужна согласованная точность по значащим цифрам, а не фиксированное количество знаков после запятой.
import java.math.*;
MathContext mc = new MathContext(4, RoundingMode.HALF_UP);
BigDecimal a = new BigDecimal("1234567");
BigDecimal b = new BigDecimal("3");
BigDecimal result = a.divide(b, mc);
System.out.println(result); // 4.115E+5 (4 significant digits)
// DECIMAL32, DECIMAL64, DECIMAL128 are standard MathContexts
BigDecimal precise = new BigDecimal("355").divide(
new BigDecimal("113"), MathContext.DECIMAL64);
System.out.println(precise); // 3.141592920353982...Пример корзины покупок
Полный расчёт для корзины покупок с использованием BigDecimal: позиции заказа, налог, скидка и итоговая сумма.
import java.math.*;
record LineItem(String name, BigDecimal price, int qty) {
BigDecimal subtotal() {
return price.multiply(new BigDecimal(qty));
}
}
var items = List.of(
new LineItem("Laptop", new BigDecimal("999.00"), 1),
new LineItem("Case", new BigDecimal("29.99"), 2),
new LineItem("Charger", new BigDecimal("49.99"), 1)
);
BigDecimal subtotal = items.stream()
.map(LineItem::subtotal)
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal taxRate = new BigDecimal("0.09");
BigDecimal tax = subtotal.multiply(taxRate).setScale(2, RoundingMode.HALF_UP);
BigDecimal total = subtotal.add(tax);
System.out.println("Subtotal: " + subtotal);
System.out.println("Tax: " + tax);
System.out.println("Total: " + total);Страйп: валюта в центах
Платёжные процессоры, такие как Страйп, хранят суммы в виде целых центов, чтобы полностью избежать проблем с десятичными числами. Преобразование между центами и отображаемыми суммами — распространённый шаблон.
import java.math.*;
// Store as cents (long) for database / API
long amountCents = 999_99L; // $999.99
// Convert to BigDecimal for display
BigDecimal amount = BigDecimal
.valueOf(amountCents)
.divide(BigDecimal.valueOf(100), 2, RoundingMode.HALF_UP);
System.out.println("Amount: $" + amount); // Amount: $999.99
// Convert user input back to cents
BigDecimal input = new BigDecimal("49.95");
long cents = input.multiply(new BigDecimal("100"))
.setScale(0, RoundingMode.HALF_UP).longValueExact();
System.out.println("Cents: " + cents); // Cents: 4995Когда использовать BigDecimal, а когда double
Выбирайте подходящий тип для каждой задачи:
- BigDecimal: денежные суммы, налоги и счета, когда важно точное представление десятичных чисел
- double: научные расчёты, графика и машинное обучение — когда важна скорость и допустимы небольшие ошибки
- int/long cents: платёжные программные интерфейсы и хранение денежных сумм в базе данных
Быстрая проверка
Какой метод сравнения BigDecimal следует использовать, чтобы проверить числовое равенство двух значений BigDecimal независимо от масштаба?
Итоги: BigDecimal для финансовых расчётов
Главные выводы:
- double/float приводят к ошибкам округления — никогда не используйте их для денежных сумм
- Создавайте BigDecimal из String или с помощью BigDecimal.valueOf(), а не из double
- Для арифметики используйте add(), subtract(), multiply() и divide()
- При делении всегда указывайте масштаб и RoundingMode
- Для проверки равенства используйте compareTo(), а не equals()
- Для работы с базами данных и программными интерфейсами рассмотрите хранение денежных сумм в виде целых центов
Часто задаваемые вопросы
Урок «BigDecimal для финансовых вычислений» бесплатный?
Да — полный текст урока «BigDecimal для финансовых вычислений» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «BigDecimal для финансовых вычислений»?
Используйте BigDecimal, чтобы избежать ошибок точности чисел с плавающей точкой при расчёте денежных сумм и налогов. Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «BigDecimal для финансовых вычислений»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Основы класса Math
- Целочисленная арифметика и переполнение
- BigDecimal для финансовых вычислений
- NumberFormat и printf