1. Обзор
В этом уроке мы продемонстрируем классы BigDecimal
и BigInteger .
Мы опишем два типа данных, их характеристики и сценарии их использования. Мы также кратко рассмотрим различные операции с использованием этих двух классов.
2. Большой десятичный
BigDecimal
представляет неизменяемое десятичное число произвольной точности со знаком . Он состоит из двух частей:
- Немасштабированное значение - произвольное целое число точности
- Масштаб - 32-битное целое число, представляющее количество цифр справа от десятичной точки.
Например, BigDecimal
3.14 имеет немасштабированное значение 314 и масштаб 2.
Мы используем BigDecimal
для высокоточной арифметики. Мы также используем его для вычислений, требующих контроля над масштабом и поведением округления . Одним из таких примеров являются расчеты, связанные с финансовыми транзакциями.
Мы можем создать объект BigDecimal
из String
, массива символов, int
, long
и BigInteger
:
@Test
public void whenBigDecimalCreated_thenValueMatches() {
BigDecimal bdFromString = new BigDecimal("0.1");
BigDecimal bdFromCharArray = new BigDecimal(new char[] {'3','.','1','6','1','5'});
BigDecimal bdlFromInt = new BigDecimal(42);
BigDecimal bdFromLong = new BigDecimal(123412345678901L);
BigInteger bigInteger = BigInteger.probablePrime(100, new Random());
BigDecimal bdFromBigInteger = new BigDecimal(bigInteger);
assertEquals("0.1",bdFromString.toString());
assertEquals("3.1615",bdFromCharArray.toString());
assertEquals("42",bdlFromInt.toString());
assertEquals("123412345678901",bdFromLong.toString());
assertEquals(bigInteger.toString(),bdFromBigInteger.toString());
}
Мы также можем создать BigDecimal
из double
:
@Test
public void whenBigDecimalCreatedFromDouble_thenValueMayNotMatch() {
BigDecimal bdFromDouble = new BigDecimal(0.1d);
assertNotEquals("0.1", bdFromDouble.toString());
}
Однако результат в этом случае отличается от ожидаемого (то есть 0,1). Это потому что:
двойной
конструктор делает точный перевод- 0.1 не имеет точного представления в
двойном
Поэтому мы должны использовать конструктор S String
вместо конструктора double
.
Кроме того, мы можем преобразовать double
и long
в BigDecimal
, используя статический метод valueOf :
@Test
public void whenBigDecimalCreatedUsingValueOf_thenValueMatches() {
BigDecimal bdFromLong1 = BigDecimal.valueOf(123412345678901L);
BigDecimal bdFromLong2 = BigDecimal.valueOf(123412345678901L, 2);
BigDecimal bdFromDouble = BigDecimal.valueOf(0.1d);
assertEquals("123412345678901", bdFromLong1.toString());
assertEquals("1234123456789.01", bdFromLong2.toString());
assertEquals("0.1", bdFromDouble.toString());
}
Этот метод преобразует double
в представление String
перед преобразованием в BigDecimal
. Кроме того, он может повторно использовать экземпляры объектов.
Следовательно, мы должны использовать метод valueOf
вместо конструкторов .
3. Операции над BigDecimal
Как и другие классы Number (
Integer
, Long
, Double
и т. д .), BigDecimal
предоставляет операции для арифметических операций и операций сравнения. Он также предоставляет операции для управления масштабом, округления и преобразования формата.
Он не перегружает арифметические (+, -, /, *) или логические (>. < и т. д.) операторы. Вместо этого мы используем соответствующие методы — сложение
, вычитание
, умножение
, деление
и сравнение.
BigDecimal
имеет методы для извлечения различных атрибутов, таких как точность, масштаб и знак :
@Test
public void whenGettingAttributes_thenExpectedResult() {
BigDecimal bd = new BigDecimal("-12345.6789");
assertEquals(9, bd.precision());
assertEquals(4, bd.scale());
assertEquals(-1, bd.signum());
}
Сравниваем значение двух BigDecimals с помощью метода compareTo
:
@Test
public void whenComparingBigDecimals_thenExpectedResult() {
BigDecimal bd1 = new BigDecimal("1.0");
BigDecimal bd2 = new BigDecimal("1.00");
BigDecimal bd3 = new BigDecimal("2.0");
assertTrue(bd1.compareTo(bd3) < 0);
assertTrue(bd3.compareTo(bd1) > 0);
assertTrue(bd1.compareTo(bd2) == 0);
assertTrue(bd1.compareTo(bd3) <= 0);
assertTrue(bd1.compareTo(bd2) >= 0);
assertTrue(bd1.compareTo(bd3) != 0);
}
Этот метод игнорирует масштаб при сравнении.
С другой стороны, метод equals
считает два объекта BigDecimal
равными, только если они равны по значению и масштабу . Таким образом, BigDecimals
1.0 и 1.00 не равны при сравнении этим методом.
@Test
public void whenEqualsCalled_thenSizeAndScaleMatched() {
BigDecimal bd1 = new BigDecimal("1.0");
BigDecimal bd2 = new BigDecimal("1.00");
assertFalse(bd1.equals(bd2));
}
Выполняем арифметические операции, вызывая соответствующие методы :
@Test
public void whenPerformingArithmetic_thenExpectedResult() {
BigDecimal bd1 = new BigDecimal("4.0");
BigDecimal bd2 = new BigDecimal("2.0");
BigDecimal sum = bd1.add(bd2);
BigDecimal difference = bd1.subtract(bd2);
BigDecimal quotient = bd1.divide(bd2);
BigDecimal product = bd1.multiply(bd2);
assertTrue(sum.compareTo(new BigDecimal("6.0")) == 0);
assertTrue(difference.compareTo(new BigDecimal("2.0")) == 0);
assertTrue(quotient.compareTo(new BigDecimal("2.0")) == 0);
assertTrue(product.compareTo(new BigDecimal("8.0")) == 0);
}
Поскольку BigDecimal
неизменяем, эти операции не изменяют существующие объекты. Скорее, они возвращают новые объекты.
4. Округление и BigDecimal
Округляя число, мы заменяем его другим, имеющим более короткое, простое и осмысленное представление . Например, мы округляем 24,784917 долларов США до 24,78 долларов США, поскольку у нас нет дробных центов.
Используемые точность и режим округления варьируются в зависимости от расчета. Например, в федеральных налоговых декларациях США указывается округление до целых сумм в долларах с использованием HALF_UP
.
Есть два класса, которые управляют поведением округления — RoundingMode
и MathContext
.
Перечисление RoundingMode
обеспечивает восемь режимов округления :
CEILING –
округление до положительной бесконечностиПОЛ –
округление до минус бесконечностиВВЕРХ –
округление от нуляВНИЗ –
округление до нуляHALF_UP — округление
к «ближайшему соседу», если только оба соседа не равноудалены, и в этом случае округление вверхHALF_DOWN — округление
к «ближайшему соседу», если только оба соседа не равноудалены, и в этом случае округление внизHALF_EVEN — округление
к «ближайшему соседу», если только оба соседа не равноудалены, и в этом случае округление к четному соседуНЕОБХОДИМО –
округление не требуется, и если точный результат невозможен, выдается исключениеArithmeticException .
Режим округления HALF_EVEN
минимизирует смещение из-за операций округления. Он часто используется. Это также известно как округление банкира .
MathContext
инкапсулирует как точность, так и режим округления . Есть несколько предопределенных MathContexts:
DECIMAL32
- точность 7 цифр и режим округления HALF_EVENDECIMAL64
- точность 16 цифр и режим округления HALF_EVENDECIMAL128
— точность до 34 цифр и режим округления HALF_EVENUNLIMITED
– арифметика с неограниченной точностью
Используя этот класс, мы можем округлить число BigDecimal
, используя заданную точность и поведение округления:
@Test
public void whenRoundingDecimal_thenExpectedResult() {
BigDecimal bd = new BigDecimal("2.5");
// Round to 1 digit using HALF_EVEN
BigDecimal rounded = bd
.round(new MathContext(1, RoundingMode.HALF_EVEN));
assertEquals("2", rounded.toString());
}
Теперь давайте рассмотрим концепцию округления, используя пример расчета.
Давайте напишем метод для расчета общей суммы, подлежащей оплате за товар, с учетом количества и цены за единицу. Давайте также применим ставку дисконтирования и ставку налога с продаж. Мы округляем окончательный результат до центов с помощью метода setScale
:
public static BigDecimal calculateTotalAmount(BigDecimal quantity,
BigDecimal unitPrice, BigDecimal discountRate, BigDecimal taxRate) {
BigDecimal amount = quantity.multiply(unitPrice);
BigDecimal discount = amount.multiply(discountRate);
BigDecimal discountedAmount = amount.subtract(discount);
BigDecimal tax = discountedAmount.multiply(taxRate);
BigDecimal total = discountedAmount.add(tax);
// round to 2 decimal places using HALF_EVEN
BigDecimal roundedTotal = total.setScale(2, RoundingMode.HALF_EVEN);
return roundedTotal;
}
Теперь давайте напишем модульный тест для этого метода:
@Test
public void givenPurchaseTxn_whenCalculatingTotalAmount_thenExpectedResult() {
BigDecimal quantity = new BigDecimal("4.5");
BigDecimal unitPrice = new BigDecimal("2.69");
BigDecimal discountRate = new BigDecimal("0.10");
BigDecimal taxRate = new BigDecimal("0.0725");
BigDecimal amountToBePaid = BigDecimalDemo
.calculateTotalAmount(quantity, unitPrice, discountRate, taxRate);
assertEquals("11.68", amountToBePaid.toString());
}
5. Большое Целое
BigInteger
представляет неизменяемые целые числа произвольной точности . Он похож на примитивные целочисленные типы, но допускает произвольные большие значения.
Он используется, когда вовлеченные целые числа превышают предел длинного
типа. Например, факториал 50 равен 304140932017133780436126081660647688443776415689605120000000000000.
Это значение слишком велико для обработки данных типа int или long .
Его можно сохранить только в переменной BigInteger .
Он широко используется в приложениях безопасности и криптографии.
Мы можем создать BigInteger
из массива байтов или
строки
:
@Test
public void whenBigIntegerCreatedFromConstructor_thenExpectedResult() {
BigInteger biFromString = new BigInteger("1234567890987654321");
BigInteger biFromByteArray = new BigInteger(
new byte[] { 64, 64, 64, 64, 64, 64 });
BigInteger biFromSignMagnitude = new BigInteger(-1,
new byte[] { 64, 64, 64, 64, 64, 64 });
assertEquals("1234567890987654321", biFromString.toString());
assertEquals("70644700037184", biFromByteArray.toString());
assertEquals("-70644700037184", biFromSignMagnitude.toString());
}
Кроме того, мы можем преобразовать long
в BigInteger
с помощью статического метода valueOf :
@Test
public void whenLongConvertedToBigInteger_thenValueMatches() {
BigInteger bi = BigInteger.valueOf(2305843009213693951L);
assertEquals("2305843009213693951", bi.toString());
}
6. Операции над BigInteger
Подобно int
и long
, BigInteger
реализует все арифметические и логические операции. Но это не перегружает операторов.
Он также реализует соответствующие методы класса Math :
abs
, min
, max
, pow
, signum
.
Мы сравниваем значение двух BigInteger с помощью метода compareTo
:
@Test
public void givenBigIntegers_whentCompared_thenExpectedResult() {
BigInteger i = new BigInteger("123456789012345678901234567890");
BigInteger j = new BigInteger("123456789012345678901234567891");
BigInteger k = new BigInteger("123456789012345678901234567892");
assertTrue(i.compareTo(i) == 0);
assertTrue(j.compareTo(i) > 0);
assertTrue(j.compareTo(k) < 0);
}
Выполняем арифметические операции, вызывая соответствующие методы:
@Test
public void givenBigIntegers_whenPerformingArithmetic_thenExpectedResult() {
BigInteger i = new BigInteger("4");
BigInteger j = new BigInteger("2");
BigInteger sum = i.add(j);
BigInteger difference = i.subtract(j);
BigInteger quotient = i.divide(j);
BigInteger product = i.multiply(j);
assertEquals(new BigInteger("6"), sum);
assertEquals(new BigInteger("2"), difference);
assertEquals(new BigInteger("2"), quotient);
assertEquals(new BigInteger("8"), product);
}
Поскольку BigInteger
неизменяем, эти операции не изменяют существующие объекты. В отличие от int
и long
, эти операции не переполняются.
BigInteger
имеет битовые операции, аналогичные int
и long
. Но нам нужно использовать методы вместо операторов:
@Test
public void givenBigIntegers_whenPerformingBitOperations_thenExpectedResult() {
BigInteger i = new BigInteger("17");
BigInteger j = new BigInteger("7");
BigInteger and = i.and(j);
BigInteger or = i.or(j);
BigInteger not = j.not();
BigInteger xor = i.xor(j);
BigInteger andNot = i.andNot(j);
BigInteger shiftLeft = i.shiftLeft(1);
BigInteger shiftRight = i.shiftRight(1);
assertEquals(new BigInteger("1"), and);
assertEquals(new BigInteger("23"), or);
assertEquals(new BigInteger("-8"), not);
assertEquals(new BigInteger("22"), xor);
assertEquals(new BigInteger("16"), andNot);
assertEquals(new BigInteger("34"), shiftLeft);
assertEquals(new BigInteger("8"), shiftRight);
}
Он имеет дополнительные методы обработки битов :
@Test
public void givenBigIntegers_whenPerformingBitManipulations_thenExpectedResult() {
BigInteger i = new BigInteger("1018");
int bitCount = i.bitCount();
int bitLength = i.bitLength();
int getLowestSetBit = i.getLowestSetBit();
boolean testBit3 = i.testBit(3);
BigInteger setBit12 = i.setBit(12);
BigInteger flipBit0 = i.flipBit(0);
BigInteger clearBit3 = i.clearBit(3);
assertEquals(8, bitCount);
assertEquals(10, bitLength);
assertEquals(1, getLowestSetBit);
assertEquals(true, testBit3);
assertEquals(new BigInteger("5114"), setBit12);
assertEquals(new BigInteger("1019"), flipBit0);
assertEquals(new BigInteger("1010"), clearBit3);
}
BigInteger
предоставляет методы для вычисления GCD и модульной арифметики :
@Test
public void givenBigIntegers_whenModularCalculation_thenExpectedResult() {
BigInteger i = new BigInteger("31");
BigInteger j = new BigInteger("24");
BigInteger k = new BigInteger("16");
BigInteger gcd = j.gcd(k);
BigInteger multiplyAndmod = j.multiply(k).mod(i);
BigInteger modInverse = j.modInverse(i);
BigInteger modPow = j.modPow(k, i);
assertEquals(new BigInteger("8"), gcd);
assertEquals(new BigInteger("12"), multiplyAndmod);
assertEquals(new BigInteger("22"), modInverse);
assertEquals(new BigInteger("7"), modPow);
}
Он также имеет методы, связанные с простой генерацией и проверкой простоты :
@Test
public void givenBigIntegers_whenPrimeOperations_thenExpectedResult() {
BigInteger i = BigInteger.probablePrime(100, new Random());
boolean isProbablePrime = i.isProbablePrime(1000);
assertEquals(true, isProbablePrime);
}
7. Заключение
В этом кратком руководстве мы рассмотрели классы BigDecimal
и BigInteger.
Они полезны для сложных числовых вычислений, когда примитивных целочисленных типов недостаточно.
Как обычно, полный исходный код можно найти на GitHub .