Skip to content

Большие числа в Java: BigInteger и BigDecimal

1. Введение

1.1 Проблемы стандартных типов

  • Ограничения типа long: от -2^63 до 2^63-1
  • Ограничения типа double: проблемы с точностью
  • Потеря точности при финансовых расчётах
  • Переполнение при больших числах

1.2 Решение

  • BigInteger: для работы с целыми числами произвольной длины
  • BigDecimal: для работы с десятичными числами произвольной точности

2. BigInteger

2.1 Создание объектов BigInteger

// Различные способы создания
BigInteger num1 = new BigInteger("123456789123456789");
BigInteger num2 = BigInteger.valueOf(123456789);
BigInteger num3 = BigInteger.ZERO;  // Константы
BigInteger num4 = BigInteger.ONE;
BigInteger num5 = BigInteger.TEN;

// Создание из других систем счисления
BigInteger fromHex = new BigInteger("FF", 16);  // 255 в шестнадцатеричной
BigInteger fromBin = new BigInteger("1010", 2); // 10 в двоичной

2.2 Основные операции

BigInteger a = new BigInteger("123456789");
BigInteger b = new BigInteger("987654321");

// Арифметические операции
BigInteger sum = a.add(b);
BigInteger diff = a.subtract(b);
BigInteger prod = a.multiply(b);
BigInteger quot = a.divide(b);
BigInteger mod = a.mod(b);

// Возведение в степень
BigInteger power = a.pow(2);

// Корень и логарифм
BigInteger sqrt = a.sqrt(); // С Java 9
int log = a.bitLength(); // Приближение двоичного логарифма

// Сравнение
int comp = a.compareTo(b); // -1, 0, или 1
boolean isEqual = a.equals(b);

2.3 Дополнительные операции

// Битовые операции
BigInteger and = a.and(b);
BigInteger or = a.or(b);
BigInteger xor = a.xor(b);
BigInteger not = a.not();
BigInteger leftShift = a.shiftLeft(2);
BigInteger rightShift = a.shiftRight(2);

// Проверка свойств
boolean isProbablePrime = a.isProbablePrime(certainty);
BigInteger nextPrime = a.nextProbablePrime();
BigInteger gcd = a.gcd(b); // НОД

// Преобразования
byte[] bytes = a.toByteArray();
String string = a.toString();
String binary = a.toString(2);
String hex = a.toString(16);

3. BigDecimal

3.1 Создание объектов BigDecimal

// Различные способы создания
BigDecimal dec1 = new BigDecimal("123.456");
BigDecimal dec2 = BigDecimal.valueOf(123.456);
BigDecimal dec3 = new BigDecimal(123.456); // Не рекомендуется!
BigDecimal dec4 = BigDecimal.ZERO;
BigDecimal dec5 = new BigDecimal("123.456789123456789");

// Создание с определённой точностью
MathContext mc = new MathContext(4); // 4 значащие цифры
BigDecimal dec6 = new BigDecimal("123.456", mc);

3.2 Основные операции

BigDecimal x = new BigDecimal("123.456");
BigDecimal y = new BigDecimal("789.012");

// Арифметические операции
BigDecimal sum = x.add(y);
BigDecimal diff = x.subtract(y);
BigDecimal prod = x.multiply(y);

// Деление (требует указания способа округления)
BigDecimal quot = x.divide(y, RoundingMode.HALF_UP);
BigDecimal quotPrec = x.divide(y, 2, RoundingMode.HALF_UP); // 2 знака после запятой

// Округление
BigDecimal rounded = x.setScale(2, RoundingMode.HALF_UP);

3.3 Режимы округления

// Доступные режимы округления
BigDecimal number = new BigDecimal("123.456789");

number.setScale(2, RoundingMode.UP);          // 123.46
number.setScale(2, RoundingMode.DOWN);        // 123.45
number.setScale(2, RoundingMode.CEILING);     // 123.46
number.setScale(2, RoundingMode.FLOOR);       // 123.45
number.setScale(2, RoundingMode.HALF_UP);     // 123.46
number.setScale(2, RoundingMode.HALF_DOWN);   // 123.46
number.setScale(2, RoundingMode.HALF_EVEN);   // 123.46

4. Точность вычислений

4.1 MathContext

// Использование MathContext для контроля точности
MathContext mc = new MathContext(4); // 4 значащие цифры
BigDecimal result = BigDecimal.ONE.divide(new BigDecimal("3"), mc); // 0.3333

// Различные контексты
MathContext mc1 = MathContext.DECIMAL32;  // 7 цифр
MathContext mc2 = MathContext.DECIMAL64;  // 16 цифр
MathContext mc3 = MathContext.DECIMAL128; // 34 цифры
MathContext mc4 = MathContext.UNLIMITED;  // Без ограничений

4.2 Примеры финансовых расчётов

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

    public BigDecimal calculateInterest(BigDecimal principal, 
                                      BigDecimal rate, 
                                      int years) {
        // Расчёт сложных процентов
        BigDecimal onePlusRate = BigDecimal.ONE.add(rate);
        BigDecimal compoundFactor = onePlusRate.pow(years);
        return principal.multiply(compoundFactor)
                       .setScale(MONEY_SCALE, ROUNDING_MODE);
    }

    public BigDecimal calculateMonthlyPayment(BigDecimal principal,
                                            BigDecimal annualRate,
                                            int years) {
        // Расчёт ежемесячного платежа по кредиту
        BigDecimal monthlyRate = annualRate.divide(BigDecimal.valueOf(12), 10, ROUNDING_MODE);
        int months = years * 12;

        BigDecimal onePlusRate = BigDecimal.ONE.add(monthlyRate);
        BigDecimal dividend = monthlyRate.multiply(principal).multiply(onePlusRate.pow(months));
        BigDecimal divisor = onePlusRate.pow(months).subtract(BigDecimal.ONE);

        return dividend.divide(divisor, MONEY_SCALE, ROUNDING_MODE);
    }
}

5. Лучшие практики

5.1 Общие рекомендации

  1. Использовать String для создания BigDecimal
  2. Всегда указывать режим округления при делении
  3. Использовать константы для scale и RoundingMode
  4. Избегать сравнения через equals()
  5. Использовать compareTo() для сравнения

5.2 Типичные ошибки

// Неправильно:
BigDecimal bad1 = new BigDecimal(0.1); // Потеря точности
BigDecimal bad2 = dec1.divide(dec2); // ArithmeticException при бесконечной дроби

// Правильно:
BigDecimal good1 = new BigDecimal("0.1");
BigDecimal good2 = dec1.divide(dec2, RoundingMode.HALF_UP);

6. Производительность и оптимизация

6.1 Рекомендации по производительности

// Оптимизация вычислений
public class PerformanceExample {
    public BigInteger calculateFactorial(int n) {
        if (n < 0) throw new IllegalArgumentException("n must be non-negative");
        if (n <= 1) return BigInteger.ONE;

        // Использование multiply() вместо цикла умножения
        return IntStream.rangeClosed(2, n)
                       .mapToObj(BigInteger::valueOf)
                       .reduce(BigInteger.ONE, BigInteger::multiply);
    }

    public BigDecimal sumPrecise(List<BigDecimal> numbers) {
        // Сортировка для минимизации ошибок округления
        return numbers.stream()
                     .sorted()
                     .reduce(BigDecimal.ZERO, BigDecimal::add);
    }
}

7. Практические примеры

7.1 Финансовые расчёты

public class FinancialCalculations {
    public static BigDecimal calculateCompoundInterest(
            BigDecimal principal,
            BigDecimal rate,
            int years,
            int compoundingPerYear) {

        BigDecimal n = new BigDecimal(compoundingPerYear);
        BigDecimal t = new BigDecimal(years);
        BigDecimal r = rate.divide(new BigDecimal(100), 10, RoundingMode.HALF_UP);

        return principal.multiply(
            BigDecimal.ONE.add(r.divide(n, 10, RoundingMode.HALF_UP))
                         .pow(years * compoundingPerYear)
        ).setScale(2, RoundingMode.HALF_UP);
    }
}

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

public class ScientificCalculations {
    public static BigDecimal calculatePi(int precision) {
        MathContext mc = new MathContext(precision);
        BigDecimal two = new BigDecimal(2);
        BigDecimal four = new BigDecimal(4);

        return four.multiply(
            BigDecimal.ONE.subtract(
                BigDecimal.ONE.divide(three, mc)
            )
        );
    }
}

ещё:

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