1. Общ преглед
В този урок ще демонстрираме класовете BigDecimal и BigInteger .
Ще опишем двата типа данни, техните характеристики и сценарии за тяхното използване. Също така ще разгледаме накратко различните операции, използвайки двата класа.
2. BigDecimal
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 tring вместо двойния конструктор .
Освен това можем да преобразуваме 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()); }
Този метод преобразува двойно в своето представяне String, преди да преобразува в BigDecimal . Освен това може да използва повторно екземпляри на обекти.
Следователно трябва да използваме метода valueOf за предпочитане пред конструкторите .
3. Операции на BigDecimal
Подобно на другите класове с числа ( 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(bd1.compareTo(bd2) == 0); assertTrue(bd1.compareTo(bd3) = 0); assertTrue(bd1.compareTo(bd3) != 0); }
Този метод игнорира скалата при сравнение.
От друга страна, на за равенство метод счита две 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 .
Режимът за закръгляване на преброяването осигурява осем режима на закръгляване:
- ТАВАН - закръглява към положителна безкрайност
- ЕТАЖ - закръглява към отрицателна безкрайност
- НАГОРЕ - закръглява се от нулата
- НАДОЛУ - закръглява се към нула
- HALF_UP - закръглява към „най-близкия съсед”, освен ако и двамата съседи са на еднакво разстояние, в този случай закръглява
- HALF_DOWN - закръглява към „най-близкия съсед“, освен ако и двамата съседи са на еднакво разстояние, в този случай закръглява надолу
- HALF_EVEN - закръглява към „най-близкия съсед“, освен ако и двамата съседи са на еднакво разстояние, в този случай закръглява към четния съсед
- НЕОБХОДИМО - не е необходимо закръгляване и се изхвърля ArithmeticException, ако не е възможен точен резултат
Режимът на закръгляване HALF_EVEN минимизира пристрастието поради операциите за закръгляване. Често се използва. Известно е и като закръгляване на банкера .
MathContext капсулира както точност, така и режим на закръгляване . Има няколко предварително дефинирани MathContexts:
- DECIMAL32 - 7 цифри с точност и режим на закръгляване HALF_EVEN
- DECIMAL64 - прецизност от 16 цифри и режим на закръгляване HALF_EVEN
- DECIMAL128 - 34 цифри с точност и режим на закръгляване HALF_EVEN
- НЕОГРАНИЧЕНО - неограничена прецизна аритметика
Използвайки този клас, можем да закръглим 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
BigInteger представлява неизменяеми цели числа с произволна точност . Той е подобен на примитивните цели числа, но позволява произволни големи стойности.
Използва се, когато участващите цели числа са по-големи от лимита за дълъг тип. Например факториалът на 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 изпълнява всички аритметични и логически операции. Но това не претоварва операторите.
Той също така прилага съответните методи от клас по математика : abs , min , max , pow , signum .
Сравняваме стойността на две BigIntegers, използвайки метода 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.