BigDecimal og BigInteger i Java

1. Oversikt

I denne veiledningen vil vi demonstrere BigDecimal og BigInteger klasser.

Vi beskriver de to datatypene, deres egenskaper og bruksscenarier. Vi vil også kort dekke de forskjellige operasjonene ved hjelp av de to klassene.

2. BigDecimal

BigDecimal representerer et uforanderlig vilkårlig presisjon signert desimaltall. Den består av to deler:

  • Uskalert verdi - et vilkårlig presisjons heltall
  • Skala - et 32-biters heltall som representerer antall sifre til høyre for desimaltegnet

For eksempel BigDecimal 3.14 har den ikke-skalerte verdien 314 og skalaen 2.

Vi bruker BigDecimal for høypresisjonsregning. Vi bruker den også til beregninger som krever kontroll over skalaen og avrunding av atferd. Et slikt eksempel er beregninger som involverer finansielle transaksjoner.

Vi kan lage en BigDecimal objekt fra String, tegnsett, int, lang, og BigInteger:

@Test offentlig ugyldig nårBigDecimalCreated_thenValueMatches () {BigDecimal bdFromString = ny BigDecimal ("0.1"); BigDecimal bdFromCharArray = ny BigDecimal (ny røyke [] {'3', '.', '1', '6', '1', '5'}); BigDecimal bdlFromInt = ny BigDecimal (42); BigDecimal bdFromLong = ny BigDecimal (123412345678901L); BigInteger bigInteger = BigInteger.probablePrime (100, new Random ()); BigDecimal bdFromBigInteger = ny 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 ()); }

Vi kan også lage BigDecimal fra dobbelt:

@Test offentlig ugyldig nårBigDecimalCreatedFromDouble_thenValueMayNotMatch () {BigDecimal bdFromDouble = ny BigDecimal (0.1d); assertNotEquals ("0.1", bdFromDouble.toString ()); }

Resultatet, i dette tilfellet, er imidlertid annerledes enn forventet (det vil si 0,1). Dette er fordi:

  • de dobbelt konstruktør gjør en nøyaktig oversettelse
  • 0.1 har ikke en nøyaktig representasjon i dobbelt

Derfor, vi burde bruke String konstruktør i stedet for dobbelt konstruktør.

I tillegg kan vi konvertere dobbelt og lang til BigInteger bruker verdien av statisk metode:

@Test offentlig ugyldig nårBigDecimalCreatedUsingValueOf_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 ()); }

Denne metoden konverterer dobbelt til dens String representasjon før konvertering til BigDecimal. I tillegg kan det gjenbruke objektforekomster.

Derfor, vi bør bruke verdien av metode fremfor konstruktørene.

3. Operasjoner på BigDecimal

Akkurat som den andre Nummer klasser (Heltall, Lang, Dobbelt etc.), BigDecimal gir operasjoner for regne- og sammenligningsoperasjoner. Det gir også operasjoner for skalmanipulering, avrunding og formatkonvertering.

Det overbelaster ikke de aritmetiske (+, -, /, *) eller logiske (>. <Etc) operatørene. I stedet bruker vi de tilsvarende metodene - legge til, trekke fra, multiplisere, dele opp og sammenligne med.

BigDecimal har metoder for å trekke ut forskjellige attributter, som presisjon, skala og tegn:

@Test offentlig ugyldig nårGettingAttributes_thenExpectedResult () {BigDecimal bd = new BigDecimal ("- 12345.6789"); assertEquals (9, bd.precision ()); assertEquals (4, bd.scale ()); assertEquals (-1, bd.signum ()); }

Vi sammenligner verdien av to BigDecimals ved hjelp av sammenligne med metode:

@Test offentlig ugyldig nårComparingBigDecimals_thenExpectedResult () {BigDecimal bd1 = ny BigDecimal ("1.0"); BigDecimal bd2 = ny BigDecimal ("1.00"); BigDecimal bd3 = ny BigDecimal ("2.0"); assertTrue (bd1.compareTo (bd3) 0); assertTrue (bd1.compareTo (bd2) == 0); assertTrue (bd1.compareTo (bd3) = 0); assertTrue (bd1.compareTo (bd3)! = 0); }

Denne metoden ignorerer skalaen mens du sammenligner.

På den andre siden, de er lik metoden vurderer to BigDecimal objekter som like bare hvis de er like i verdi og skala. Og dermed, BigDecimals 1.0 og 1.00 er ikke like sammenlignet med denne metoden.

@Test offentlig ugyldig nårEqualsCalled_thenSizeAndScaleMatched () {BigDecimal bd1 = ny BigDecimal ("1.0"); BigDecimal bd2 = ny BigDecimal ("1.00"); assertFalse (bd1.equals (bd2)); }

Vi utfører aritmetiske operasjoner ved å kalle de tilsvarende metodene:

@Test offentlig ugyldig nårPerformingArithmetic_thenExpectedResult () {BigDecimal bd1 = ny BigDecimal ("4.0"); BigDecimal bd2 = ny BigDecimal ("2.0"); BigDecimal sum = bd1.add (bd2); BigDecimal forskjell = bd1.subtract (bd2); BigDecimal kvotient = bd1.divide (bd2); BigDecimal produkt = bd1.multiply (bd2); assertTrue (sum.compareTo (ny BigDecimal ("6.0")) == 0); assertTrue (difference.compareTo (new BigDecimal ("2.0")) == 0); assertTrue (quotient.compareTo (ny BigDecimal ("2.0")) == 0); assertTrue (product.compareTo (ny BigDecimal ("8.0")) == 0); }

Siden BigDecimal er uforanderlig, endrer ikke disse operasjonene de eksisterende objektene. Snarere returnerer de nye gjenstander.

4. Avrunding og BigDecimal

Ved å avrunde et tall erstatter vi det med et annet som har kortere, enklere og mer meningsfull representasjon. For eksempel runder vi $ 24,784917 til $ 24,78 ettersom vi ikke har brøkdeler.

Presisjons- og avrundingsmodus som skal brukes, varierer avhengig av beregningen. For eksempel angir amerikanske føderale selvangivelser å avrunde til hele dollarbeløp ved hjelp av HALF_UP.

Det er to klasser som styrer avrundingsadferd - RoundingMode og MathContext.

De enum RoundingMode gir åtte avrundingsmodi:

  • TAK - runder mot positiv uendelig
  • GULV - runder mot negativ uendelig
  • OPP - runder fra null
  • NED - runder mot null
  • HALF_UP - runder mot "nærmeste nabo" med mindre begge naboene er like langt, i så fall runder opp
  • HALF_DOWN - runder mot "nærmeste nabo" med mindre begge naboene er like langt, i så fall avrunder
  • HALF_EVEN - runder mot "nærmeste nabo" med mindre begge naboene er like langt, i så fall runder mot den jevne nabo
  • UNNØDVENDIG - ingen avrunding er nødvendig og Aritmetisk unntak kastes hvis ikke noe eksakt resultat er mulig

HALF_EVEN avrundingsmodus minimerer forspenningen på grunn av avrundingsoperasjoner. Det brukes ofte. Det er også kjent som bankers avrunding.

MathContext innkapsler både presisjons- og avrundingsmodus. Det er få forhåndsdefinerte MathContexts:

  • AVSLUTTENDE 32 - 7 sifre presisjon og en avrundingsmodus på HALF_EVEN
  • AVTAL 64 - 16 sifre presisjon og en avrundingsmodus på HALF_EVEN
  • AVTAL128 - 34 sifre presisjon og en avrundingsmodus på HALF_EVEN
  • UBEGRENSET - ubegrenset presisjonsregning

Ved hjelp av denne klassen kan vi runde a BigDecimal nummer ved bruk av spesifisert presisjon og avrundingsadferd:

@Test offentlig ugyldig nårRoundingDecimal_thenExpectedResult () {BigDecimal bd = new BigDecimal ("2.5"); // Rund til 1 siffer ved hjelp av HALF_EVEN BigDecimal avrundet = bd .round (ny MathContext (1, RoundingMode.HALF_EVEN)); assertEquals ("2", rounded.toString ()); }

La oss nå undersøke avrundingskonseptet ved hjelp av en prøveberegning.

La oss skrive en metode for å beregne det totale beløpet som skal betales for en vare gitt en mengde og enhetspris. La oss også bruke en diskonteringsrente og merverdiavgift. Vi avrunder det endelige resultatet til øre ved å bruke setScale metode:

offentlig statisk BigDecimal calcTotalAmount (BigDecimal mengde, BigDecimal unitPrice, BigDecimal discountRate, BigDecimal taxRate) {BigDecimal beløp = kvantitet. multiply (unitPrice); BigDecimal rabatt = sum.multiply (discountRate); BigDecimal discountedAmount = amount.subtract (rabatt); BigDecimal tax = discountedAmount.multiply (taxRate); BigDecimal total = discountedAmount.add (skatt); // rund til 2 desimaler ved hjelp av HALF_EVEN BigDecimal roundedTotal = total.setScale (2, RoundingMode.HALF_EVEN); retur avrundetTotal; }

La oss nå skrive en enhetstest for denne metoden:

@Test offentlig ugyldig givenPurchaseTxn_whenCalculatingTotalAmount_thenExpectedResult () {BigDecimal kvantitet = ny BigDecimal ("4,5"); BigDecimal unitPrice = new BigDecimal ("2.69"); BigDecimal discountRate = ny BigDecimal ("0,10"); BigDecimal taxRate = ny BigDecimal ("0,0725"); BigDecimal amountToBePaid = BigDecimalDemo .calculateTotalAmount (mengde, enhetPris, rabattpris, taxRate); assertEquals ("11.68", amountToBePaid.toString ()); }

5. BigInteger

BigInteger representerer uforanderlige heltall med vilkårlig presisjon. Det ligner på de primitive heltalstypene, men tillater vilkårlige store verdier.

Den brukes når involverte heltall er større enn grensen på lang type. For eksempel er faktoren på 50 30414093201713378043612608166064768844377641568960512000000000000. Denne verdien er for stor for en int eller lang datatype å håndtere. Den kan bare lagres i en BigInteger variabel.

Det er mye brukt i sikkerhets- og kryptografiapplikasjoner.

Vi kan skape BigInteger fra en byte array eller String:

@Test offentlig ugyldig nårBigIntegerCreatedFromConstructor_thenExpectedResult () {BigInteger biFromString = new BigInteger ("1234567890987654321"); BigInteger biFromByteArray = nytt BigInteger (ny byte [] {64, 64, 64, 64, 64, 64}); BigInteger biFromSignMagnitude = nytt BigInteger (-1, ny byte [] {64, 64, 64, 64, 64, 64}); assertEquals ("1234567890987654321", biFromString.toString ()); assertEquals ("70644700037184", biFromByteArray.toString ()); assertEquals ("- 70644700037184", biFromSignMagnitude.toString ()); }

I tillegg, vi kan konvertere en lang til BigInteger ved hjelp av den statiske metoden verdien av:

@Test offentlig ugyldig nårLongConvertedToBigInteger_thenValueMatches () {BigInteger bi = BigInteger.valueOf (2305843009213693951L); assertEquals ("2305843009213693951", bi.toString ()); }

6. Operasjoner på BigInteger

Lik int og lang, BigInteger implementerer alle de aritmetiske og logiske operasjonene. Men det overbelaster ikke operatørene.

Den implementerer også de tilsvarende metodene fra Matte klasse: abs, min, maks, pow, signum.

Vi sammenligner verdien av to BigIntegers ved hjelp av sammenligne med metode:

@Test offentlig ugyldig 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); }

Vi utfører aritmetiske operasjoner ved å kalle de tilsvarende metodene:

@Test offentlig ugyldig givenBigIntegers_whenPerformingArithmetic_thenExpectedResult () {BigInteger i = new BigInteger ("4"); BigInteger j = nytt BigInteger ("2"); BigInteger sum = i.add (j); BigInteger difference = i.subtract (j); BigInteger kvotient = i. Divide (j); BigInteger-produkt = i.multiply (j); assertEquals (nytt BigInteger ("6"), sum); assertEquals (nytt BigInteger ("2"), forskjell); assertEquals (nytt BigInteger ("2"), kvotient); assertEquals (nytt BigInteger ("8"), produkt); }

Som BigInteger er uforanderlig, disse operasjonene endrer ikke de eksisterende objektene. I motsetning til, int og lang, disse operasjonene overløper ikke.

BigInteger har bitoperasjoner som ligner på int og lang. Men vi må bruke metodene i stedet for operatører:

@Test offentlig ugyldig givenBigIntegers_whenPerformingBitOperations_thenExpectedResult () {BigInteger i = new BigInteger ("17"); BigInteger j = nytt BigInteger ("7"); BigInteger og = i.and (j); BigInteger eller = i.or (j); BigInteger ikke = j.not (); BigInteger xor = i.xor (j); BigInteger andNot = i.andNot (j); BigInteger shiftLeft = i.shiftLeft (1); BigInteger shiftRight = i.shiftRight (1); assertEquals (nytt BigInteger ("1") og); assertEquals (nytt BigInteger ("23"), eller); assertEquals (ny BigInteger ("- 8"), ikke); assertEquals (nye BigInteger ("22"), xor); assertEquals (nytt BigInteger ("16"), og ikke); assertEquals (nytt BigInteger ("34"), shiftLeft); assertEquals (nytt BigInteger ("8"), shiftRight); }

Den har flere metoder for bitmanipulering:

@Test offentlig ugyldig givenBigIntegers_whenPerformingBitManipulations_thenExpectedResult () {BigInteger i = new BigInteger ("1018"); int bitCount = i.bitCount (); int bitLength = i.bitLength (); int getLowestSetBit = i.getLowestSetBit (); boolsk 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 (nytt BigInteger ("5114"), setBit12); assertEquals (nytt BigInteger ("1019"), flipBit0); assertEquals (nytt BigInteger ("1010"), clearBit3); }

BigInteger gir metoder for GCD-beregning og modulær aritmetikk:

@Test offentlig ugyldig gittBigIntegers_whenModularCalculation_thenExpectedResult () {BigInteger i = new BigInteger ("31"); BigInteger j = nytt BigInteger ("24"); BigInteger k = nytt 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 (nytt BigInteger ("8"), gcd); assertEquals (nye BigInteger ("12"), multiplyAndmod); assertEquals (nytt BigInteger ("22"), modInverse); assertEquals (nytt BigInteger ("7"), modPow); }

Det har også metoder relatert til primærgenerering og primality testing:

@Test offentlig ugyldig givenBigIntegers_whenPrimeOperations_thenExpectedResult () {BigInteger i = BigInteger.probablePrime (100, new Random ()); boolsk isProbablePrime = i.isProbablePrime (1000); assertEquals (true, isProbablePrime); }

7. Konklusjon

I denne raske opplæringen utforsket vi klassene BigDecimal og BigInteger. De er nyttige for avanserte numeriske beregninger der primitive heltallstyper ikke er tilstrekkelig.

Som vanlig finner du full kildekode på GitHub.