Skip to content

Практические примеры использования BigInteger и BigDecimal

1. Банковская система

1.1 Управление счетами

public class BankAccount {
    private String accountNumber;
    private BigDecimal balance;
    private static final int MONEY_SCALE = 2;
    private static final RoundingMode ROUNDING_MODE = RoundingMode.HALF_EVEN;

    public BankAccount(String accountNumber, BigDecimal initialBalance) {
        this.accountNumber = accountNumber;
        this.balance = initialBalance.setScale(MONEY_SCALE, ROUNDING_MODE);
    }

    public synchronized void deposit(BigDecimal amount) {
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("Deposit amount must be positive");
        }
        balance = balance.add(amount).setScale(MONEY_SCALE, ROUNDING_MODE);
    }

    public synchronized void withdraw(BigDecimal amount) {
        if (amount.compareTo(BigDecimal.ZERO) <= 0) {
            throw new IllegalArgumentException("Withdrawal amount must be positive");
        }
        if (balance.compareTo(amount) < 0) {
            throw new InsufficientFundsException("Insufficient funds");
        }
        balance = balance.subtract(amount).setScale(MONEY_SCALE, ROUNDING_MODE);
    }

    public BigDecimal getBalance() {
        return balance;
    }
}

1.2 Расчёт кредитов

public class LoanCalculator {
    private static final int CALCULATION_SCALE = 10;
    private static final int DISPLAY_SCALE = 2;
    private static final RoundingMode ROUNDING_MODE = RoundingMode.HALF_EVEN;

    public LoanSchedule calculateMortgage(BigDecimal principal, 
                                        BigDecimal annualRate, 
                                        int years) {
        // Месячная процентная ставка
        BigDecimal monthlyRate = annualRate
            .divide(BigDecimal.valueOf(100), CALCULATION_SCALE, ROUNDING_MODE)
            .divide(BigDecimal.valueOf(12), CALCULATION_SCALE, ROUNDING_MODE);

        int totalPayments = years * 12;

        // Расчёт ежемесячного платежа
        BigDecimal monthlyPayment = calculateMonthlyPayment(
            principal, monthlyRate, totalPayments);

        List<PaymentPeriod> schedule = new ArrayList<>();
        BigDecimal remainingPrincipal = principal;

        for (int month = 1; month <= totalPayments; month++) {
            // Расчёт процентов за период
            BigDecimal interest = remainingPrincipal
                .multiply(monthlyRate)
                .setScale(CALCULATION_SCALE, ROUNDING_MODE);

            // Расчёт основного долга за период
            BigDecimal principalPaid = monthlyPayment.subtract(interest);

            // Обновление остатка
            remainingPrincipal = remainingPrincipal
                .subtract(principalPaid)
                .setScale(CALCULATION_SCALE, ROUNDING_MODE);

            schedule.add(new PaymentPeriod(
                month,
                monthlyPayment.setScale(DISPLAY_SCALE, ROUNDING_MODE),
                principalPaid.setScale(DISPLAY_SCALE, ROUNDING_MODE),
                interest.setScale(DISPLAY_SCALE, ROUNDING_MODE),
                remainingPrincipal.setScale(DISPLAY_SCALE, ROUNDING_MODE)
            ));
        }

        return new LoanSchedule(schedule);
    }

    private BigDecimal calculateMonthlyPayment(
            BigDecimal principal, 
            BigDecimal monthlyRate, 
            int totalPayments) {

        BigDecimal onePlusRate = BigDecimal.ONE.add(monthlyRate);
        BigDecimal rateFactors = onePlusRate.pow(totalPayments);

        return principal
            .multiply(monthlyRate)
            .multiply(rateFactors)
            .divide(rateFactors.subtract(BigDecimal.ONE), 
                   CALCULATION_SCALE, ROUNDING_MODE);
    }
}

2. Система электронной коммерции

2.1 Расчёт скидок и налогов

public class PriceCalculator {
    private static final int PRICE_SCALE = 2;
    private static final RoundingMode ROUNDING_MODE = RoundingMode.HALF_EVEN;

    public OrderTotal calculateOrderTotal(List<OrderItem> items, 
                                        BigDecimal taxRate,
                                        List<Discount> discounts) {
        // Подсчёт суммы заказа
        BigDecimal subtotal = items.stream()
            .map(item -> item.getPrice().multiply(new BigDecimal(item.getQuantity())))
            .reduce(BigDecimal.ZERO, BigDecimal::add)
            .setScale(PRICE_SCALE, ROUNDING_MODE);

        // Применение скидок
        BigDecimal discountAmount = discounts.stream()
            .map(discount -> calculateDiscount(subtotal, discount))
            .reduce(BigDecimal.ZERO, BigDecimal::add)
            .setScale(PRICE_SCALE, ROUNDING_MODE);

        BigDecimal discountedTotal = subtotal.subtract(discountAmount);

        // Расчёт налога
        BigDecimal tax = discountedTotal
            .multiply(taxRate)
            .divide(BigDecimal.valueOf(100), ROUNDING_MODE)
            .setScale(PRICE_SCALE, ROUNDING_MODE);

        // Итоговая сумма
        BigDecimal total = discountedTotal.add(tax);

        return new OrderTotal(subtotal, discountAmount, tax, total);
    }

    private BigDecimal calculateDiscount(BigDecimal amount, Discount discount) {
        switch (discount.getType()) {
            case PERCENTAGE:
                return amount
                    .multiply(discount.getValue()
                    .divide(BigDecimal.valueOf(100), ROUNDING_MODE));
            case FIXED:
                return discount.getValue();
            default:
                throw new IllegalArgumentException("Unknown discount type");
        }
    }
}

3. Криптографические вычисления

3.1 Реализация RSA

public class RSAImplementation {
    public static class KeyPair {
        private final BigInteger publicKey;
        private final BigInteger privateKey;
        private final BigInteger modulus;

        public KeyPair(BigInteger publicKey, BigInteger privateKey, BigInteger modulus) {
            this.publicKey = publicKey;
            this.privateKey = privateKey;
            this.modulus = modulus;
        }
    }

    public KeyPair generateKeyPair(int bitLength) {
        SecureRandom random = new SecureRandom();

        // Генерация простых чисел
        BigInteger p = BigInteger.probablePrime(bitLength / 2, random);
        BigInteger q = BigInteger.probablePrime(bitLength / 2, random);

        // Вычисление модуля
        BigInteger n = p.multiply(q);

        // Вычисление функции Эйлера
        BigInteger phi = p.subtract(BigInteger.ONE)
                         .multiply(q.subtract(BigInteger.ONE));

        // Выбор открытой экспоненты
        BigInteger e = BigInteger.valueOf(65537);

        // Вычисление закрытой экспоненты
        BigInteger d = e.modInverse(phi);

        return new KeyPair(e, d, n);
    }

    public BigInteger encrypt(BigInteger message, 
                            BigInteger publicKey, 
                            BigInteger modulus) {
        return message.modPow(publicKey, modulus);
    }

    public BigInteger decrypt(BigInteger ciphertext, 
                            BigInteger privateKey, 
                            BigInteger modulus) {
        return ciphertext.modPow(privateKey, modulus);
    }
}

4. Научные вычисления

4.1 Расчёт статистических показателей

public class StatisticalCalculator {
    private static final int CALCULATION_SCALE = 10;
    private static final int DISPLAY_SCALE = 4;
    private static final RoundingMode ROUNDING_MODE = RoundingMode.HALF_EVEN;

    public StatisticalResult calculate(List<BigDecimal> values) {
        int n = values.size();
        if (n == 0) {
            throw new IllegalArgumentException("Empty dataset");
        }

        // Среднее значение
        BigDecimal sum = values.stream()
            .reduce(BigDecimal.ZERO, BigDecimal::add);

        BigDecimal mean = sum
            .divide(BigDecimal.valueOf(n), CALCULATION_SCALE, ROUNDING_MODE);

        // Дисперсия
        BigDecimal variance = values.stream()
            .map(x -> x.subtract(mean)
                      .pow(2))
            .reduce(BigDecimal.ZERO, BigDecimal::add)
            .divide(BigDecimal.valueOf(n), CALCULATION_SCALE, ROUNDING_MODE);

        // Стандартное отклонение
        BigDecimal stdDev = sqrt(variance, CALCULATION_SCALE);

        return new StatisticalResult(
            mean.setScale(DISPLAY_SCALE, ROUNDING_MODE),
            variance.setScale(DISPLAY_SCALE, ROUNDING_MODE),
            stdDev.setScale(DISPLAY_SCALE, ROUNDING_MODE)
        );
    }

    private BigDecimal sqrt(BigDecimal value, int scale) {
        BigDecimal two = BigDecimal.valueOf(2);
        BigDecimal precision = BigDecimal.valueOf(0.1)
            .movePointLeft(scale);

        BigDecimal result = BigDecimal.valueOf(
            Math.sqrt(value.doubleValue()));

        while (true) {
            BigDecimal next = result.add(value
                .divide(result, scale, ROUNDING_MODE))
                .divide(two, scale, ROUNDING_MODE);

            if (result.subtract(next).abs().compareTo(precision) <= 0) {
                return result;
            }
            result = next;
        }
    }
}

5. Бухгалтерские расчёты

5.1 Расчёт амортизации

public class DepreciationCalculator {
    private static final int MONEY_SCALE = 2;
    private static final RoundingMode ROUNDING_MODE = RoundingMode.HALF_EVEN;

    public List<DepreciationPeriod> calculateStraightLine(
            BigDecimal initialValue,
            BigDecimal salvageValue,
            int lifeYears) {

        List<DepreciationPeriod> schedule = new ArrayList<>();

        // Годовая амортизация
        BigDecimal annualDepreciation = initialValue
            .subtract(salvageValue)
            .divide(BigDecimal.valueOf(lifeYears), 
                   MONEY_SCALE, ROUNDING_MODE);

        BigDecimal currentValue = initialValue;

        for (int year = 1; year <= lifeYears; year++) {
            currentValue = currentValue.subtract(annualDepreciation);

            schedule.add(new DepreciationPeriod(
                year,
                annualDepreciation,
                currentValue.setScale(MONEY_SCALE, ROUNDING_MODE)
            ));
        }

        return schedule;
    }

    public List<DepreciationPeriod> calculateDecliningBalance(
            BigDecimal initialValue,
            BigDecimal salvageValue,
            int lifeYears,
            BigDecimal rate) {

        List<DepreciationPeriod> schedule = new ArrayList<>();
        BigDecimal currentValue = initialValue;

        for (int year = 1; year <= lifeYears; year++) {
            BigDecimal depreciation = currentValue
                .multiply(rate)
                .setScale(MONEY_SCALE, ROUNDING_MODE);

            if (currentValue.subtract(depreciation)
                    .compareTo(salvageValue) < 0) {
                depreciation = currentValue.subtract(salvageValue);
                currentValue = salvageValue;
            } else {
                currentValue = currentValue.subtract(depreciation);
            }

            schedule.add(new DepreciationPeriod(
                year,
                depreciation,
                currentValue
            ));

            if (currentValue.compareTo(salvageValue) <= 0) {
                break;
            }
        }

        return schedule;
    }
}

Эти примеры демонстрируют реальные сценарии использования BigInteger и BigDecimal в различных областях. Хотели бы вы: 1. Рассмотреть другие области применения? 2. Углубиться в детали реализации какого-то конкретного примера? 3. Обсудить оптимизацию производительности этих решений? 4. Добавить обработку ошибок и валидацию данных?