Большие числа в 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 Общие рекомендации
- Использовать String для создания BigDecimal
- Всегда указывать режим округления при делении
- Использовать константы для scale и RoundingMode
- Избегать сравнения через equals()
- Использовать 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]]