1. Обзор
В этом уроке мы продемонстрируем BigDecimal и BigInteger | классы.
Мы опишем два типа данных, их характеристики и сценарии их использования. Мы также кратко рассмотрим различные операции, использующие эти два класса.
2. Большой десятичный
BigDecimal представляет собой неизменяемое десятичное число со знаком произвольной точности . Он состоит из двух частей:
- Значение без масштаба – целое число произвольной точности
- Масштаб – 32-разрядное целое число, представляющее количество цифр справа от десятичной точки
Например, BigDecimal 3.14 имеет значение без масштаба 314 и масштаб 2.
Мы используем BigDecimal для высокоточной арифметики. Мы также используем его для расчетов, требующих контроля над масштабом и округлением поведения . Одним из таких примеров являются расчеты, связанные с финансовыми операциями.
Мы можем создать BigDecimal объект из Строки , массива символов, 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). Это потому что:
- конструктор double выполняет точный перевод
- 0.1 не имеет точного представления в double
Поэтому мы должны использовать конструктор S string вместо double конструктора .
Кроме того, мы можем преобразовать double и long в BigInteger с помощью 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 в его Строковое представление перед преобразованием в BigDecimal . Кроме того, он может повторно использовать экземпляры объектов.
Следовательно, мы должны использовать метод valueOf , отдавая предпочтение конструкторам .
3. Операции с большой десятичной
Как и другие Числовые классы ( Целочисленные , Длинные , Двойные и т.д.), 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()); }
Мы сравниваем значение двух больших десятичных чисел с помощью метода 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); }
Этот метод игнорирует масштаб при сравнении.
С другой стороны, метод равно рассматривает два больших десятичных объекта как равные, только если они равны по значению и масштабу . Таким образом, 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. Округление и большая десятичная
Округляя число, мы заменяем его другим, имеющим более короткое, простое и осмысленное представление . Например, мы округляем 24,784917 доллара до 24,78 доллара, так как у нас нет дробных центов.
Используемая точность и режим округления зависят от результатов расчета. Например, в федеральных налоговых декларациях США указывается округление до целых сумм в долларах с использованием HALF_UP .
Существует два класса, которые управляют поведением округления – Режим округления и MathContext .
Режим перечисления Округления обеспечивает восемь режимов округления:
- ПОТОЛОК – округляется в сторону положительной бесконечности
- ЭТАЖ – округляется в сторону отрицательной бесконечности
- ВВЕРХ – округляется от нуля
- ВНИЗ – округляется до нуля
- HALF_UP – округляется в сторону “ближайшего соседа”, если только оба соседа не находятся на одинаковом расстоянии, и в этом случае округляется
- HALF_DOWN – округляется в сторону “ближайшего соседа”, если только оба соседа не находятся на равном расстоянии, и в этом случае округляется в меньшую сторону
- HALF_EVEN – округляется в сторону “ближайшего соседа”, если только оба соседа не находятся на равном расстоянии, и в этом случае округляется в сторону четного соседа
- НЕНУЖНО – округление не требуется и Исключение ArithmeticException выдается, если точный результат невозможен
Режим округления HALF_EVEN минимизирует смещение из-за операций округления. Он часто используется. Это также известно как округление банкира .
MathContext инкапсулирует как режим точности, так и режим округления . Существует несколько предопределенных Mathcontext:
- ДЕСЯТИЧНАЯ ДРОБЬ32 – точность 7 цифр и режим округления в ПОЛ-НУЛЯ
- ДЕСЯТИЧНАЯ ДРОБЬ64 – точность 16 цифр и режим округления в два раза меньше
- ДЕСЯТИЧНОЕ ЧИСЛО 128 – Точность 34 цифр и режим округления в ПОЛ-НУЛЯ
- НЕОГРАНИЧЕННАЯ – неограниченная точность арифметики
Используя этот класс, мы можем округлить 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 представляет неизменяемые целые числа произвольной точности . Он похож на примитивные целочисленные типы, но допускает произвольные большие значения.
Он используется, когда задействованные целые числа превышают предел типа long . Например, факториал 50 равен 30414093201713378043612608166064768844377641568960512000000000000. Это значение слишком велико для обработки типа данных 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 .
Мы сравниваем значение двух больших целых чисел, используя сравнение метод:
@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 .