0Pricing
Java Academy · Урок

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 — локальная установка не требуется.

Все уроки этого курса

  1. Основы класса Math
  2. Целочисленная арифметика и переполнение
  3. BigDecimal для финансовых вычислений
  4. NumberFormat и printf
← Назад к Java Academy