BigDecimal und BigInteger in Java

1. Übersicht

In diesem Tutorial werden wir die Klassen BigDecimal und BigInteger demonstrieren .

Wir werden die beiden Datentypen, ihre Eigenschaften und ihre Verwendungsszenarien beschreiben. Wir werden auch kurz die verschiedenen Operationen anhand der beiden Klassen behandeln.

2. BigDecimal

BigDecimal repräsentiert eine unveränderliche vorzeichenbehaftete Dezimalzahl mit beliebiger Genauigkeit . Es besteht aus zwei Teilen:

  • Unskalierter Wert - eine Ganzzahl mit beliebiger Genauigkeit
  • Skalieren - Eine 32-Bit-Ganzzahl, die die Anzahl der Stellen rechts vom Dezimalpunkt darstellt

Zum Beispiel hat der BigDecimal 3.14 den nicht skalierten Wert 314 und die Skala 2.

Wir verwenden BigDecimal für hochpräzise Arithmetik. Wir verwenden es auch für Berechnungen, die eine Kontrolle über die Skalierung und das Abrunden des Verhaltens erfordern . Ein solches Beispiel sind Berechnungen mit Finanztransaktionen.

Wir können ein BigDecimal- Objekt aus String , Zeichenarray, int , long und BigInteger erstellen :

@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()); }

Wir können BigDecimal auch aus double erstellen :

@Test public void whenBigDecimalCreatedFromDouble_thenValueMayNotMatch() { BigDecimal bdFromDouble = new BigDecimal(0.1d); assertNotEquals("0.1", bdFromDouble.toString()); }

Das Ergebnis unterscheidet sich in diesem Fall jedoch von den erwarteten (dh 0,1). Das ist weil:

  • Der Doppelkonstruktor führt eine genaue Übersetzung durch
  • 0.1 hat keine exakte Darstellung im Doppel

Daher sollten wir den S tring- Konstruktor anstelle des Doppelkonstruktors verwenden .

Darüber hinaus können wir double und long mit der statischen Methode valueOf in BigInteger konvertieren :

@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()); }

Diese Methode konvertiert double in ihre String- Darstellung, bevor sie in BigDecimal konvertiert wird . Darüber hinaus können Objektinstanzen wiederverwendet werden.

Daher sollten wir die valueOf- Methode den Konstruktoren vorziehen .

3. Operationen auf BigDecimal

Genau wie die anderen Zahlenklassen ( Integer , Long , Double usw.) bietet BigDecimal Operationen für Arithmetik- und Vergleichsoperationen. Es bietet auch Operationen zur Skalierung, Rundung und Formatkonvertierung.

Die arithmetischen (+, -, /, *) oder logischen (>. <Usw.) Operatoren werden nicht überladen. Stattdessen verwenden wir die entsprechenden Methoden - addieren , subtrahieren , multiplizieren , dividieren und vergleichen.

BigDecimal verfügt über Methoden zum Extrahieren verschiedener Attribute wie Genauigkeit, Skalierung und Vorzeichen :

@Test public void whenGettingAttributes_thenExpectedResult() { BigDecimal bd = new BigDecimal("-12345.6789"); assertEquals(9, bd.precision()); assertEquals(4, bd.scale()); assertEquals(-1, bd.signum()); }

Wir vergleichen den Wert von zwei BigDecimals mit der compareTo- Methode :

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

Diese Methode ignoriert die Skala beim Vergleichen.

Andererseits betrachtet die Methode equals zwei BigDecimal- Objekte nur dann als gleich, wenn sie in Wert und Skalierung gleich sind . Daher sind BigDecimals 1.0 und 1.00 im Vergleich zu dieser Methode nicht gleich.

@Test public void whenEqualsCalled_thenSizeAndScaleMatched() { BigDecimal bd1 = new BigDecimal("1.0"); BigDecimal bd2 = new BigDecimal("1.00"); assertFalse(bd1.equals(bd2)); }

Wir führen arithmetische Operationen durch, indem wir die entsprechenden Methoden aufrufen :

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

Da BigDecimal unveränderlich ist, ändern diese Vorgänge die vorhandenen Objekte nicht. Sie geben vielmehr neue Objekte zurück.

4. Rundung und BigDecimal

Indem wir eine Zahl runden, ersetzen wir sie durch eine andere mit einer kürzeren, einfacheren und aussagekräftigeren Darstellung . Zum Beispiel runden wir 24,784917 USD auf 24,78 USD, da wir keine gebrochenen Cent haben.

Der zu verwendende Präzisions- und Rundungsmodus hängt von der Berechnung ab. In US-Steuererklärungen wird beispielsweise angegeben, dass mit HALF_UP auf ganze Dollarbeträge gerundet werden soll .

Es gibt zwei Klassen, die das Rundungsverhalten steuern - RoundingMode und MathContext .

Der Enum RoundingMode bietet acht Rundungsmodi:

  • DECKEN - Runden in Richtung positive Unendlichkeit
  • BODEN - rundet in Richtung negative Unendlichkeit
  • UP - rundet von Null weg
  • DOWN - rundet gegen Null
  • HALF_UP - rundet auf "nächster Nachbar", es sei denn, beide Nachbarn sind gleich weit entfernt. In diesem Fall wird aufgerundet
  • HALF_DOWN - rundet auf "nächster Nachbar", es sei denn, beide Nachbarn sind gleich weit entfernt. In diesem Fall wird abgerundet
  • HALF_EVEN - rundet auf den „nächsten Nachbarn“ ab, es sei denn, beide Nachbarn sind gleich weit entfernt. In diesem Fall wird auf den geraden Nachbarn gerundet
  • NICHT NOTWENDIG - Es ist keine Rundung erforderlich und ArithmeticException wird ausgelöst, wenn kein genaues Ergebnis möglich ist

Der Rundungsmodus HALF_EVEN minimiert die Vorspannung aufgrund von Rundungsoperationen. Es wird häufig verwendet. Es ist auch als Rundung des Bankiers bekannt .

MathContext kapselt sowohl den Präzisions- als auch den Rundungsmodus . Es gibt nur wenige vordefinierte MathContexts:

  • DECIMAL32 - 7- stellige Genauigkeit und ein Rundungsmodus von HALF_EVEN
  • DECIMAL64 - 16- stellige Genauigkeit und ein Rundungsmodus von HALF_EVEN
  • DECIMAL128 - 34- stellige Genauigkeit und ein Rundungsmodus von HALF_EVEN
  • UNLIMITED - Arithmetik mit unbegrenzter Präzision

Mit dieser Klasse können wir eine BigDecimal- Zahl mit der angegebenen Genauigkeit und dem angegebenen Rundungsverhalten runden:

@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()); }

Lassen Sie uns nun das Rundungskonzept anhand einer Beispielberechnung untersuchen.

Schreiben wir eine Methode zur Berechnung des Gesamtbetrags, der für einen Artikel bei gegebener Menge und Einheitspreis zu zahlen ist. Wenden wir auch einen Abzinsungssatz und einen Umsatzsteuersatz an. Wir runden das Endergebnis mit der setScale- Methode auf Cent ab :

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; }

Schreiben wir nun einen Komponententest für diese Methode:

@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 repräsentiert unveränderliche Ganzzahlen mit beliebiger Genauigkeit . Es ähnelt den primitiven Ganzzahltypen, erlaubt jedoch beliebig große Werte.

Es wird verwendet, wenn die beteiligten Ganzzahlen größer als die Grenze des langen Typs sind. Die Fakultät 50 lautet beispielsweise 30414093201713378043612608166064768844377641568960512000000000000. Dieser Wert ist zu groß für einen int- oder langen Datentyp. Es kann nur in einer BigInteger- Variablen gespeichert werden.

Es ist weit verbreitet in Sicherheits- und Kryptografieanwendungen.

Wir können BigInteger aus einem Byte- Array oder einem String erstellen :

@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()); }

Darüber hinaus können wir ein Long mit der statischen Methode valueOf in BigInteger konvertieren :

@Test public void whenLongConvertedToBigInteger_thenValueMatches() { BigInteger bi = BigInteger.valueOf(2305843009213693951L); assertEquals("2305843009213693951", bi.toString()); }

6. Operationen auf BigInteger

Ähnlich wie int und lange , BigInteger implementiert alle arithmetischen und logischen Operationen. Die Bediener werden jedoch nicht überlastet.

Es implementiert auch die entsprechenden Methoden aus der Matheklasse : abs , min , max , pow , signum .

Wir vergleichen den Wert von zwei BigIntegern mit der compareTo- Methode:

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

Wir führen arithmetische Operationen durch, indem wir die entsprechenden Methoden aufrufen:

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

Da BigInteger unveränderlich ist, ändern diese Vorgänge die vorhandenen Objekte nicht. Im Gegensatz zu , int und lange , machen diese Vorgänge nicht überläuft.

BigInteger hat ähnliche Bitoperationen wie int und long . Wir müssen jedoch die Methoden anstelle von Operatoren verwenden:

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

Es gibt zusätzliche Bitmanipulationsmethoden :

@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 bietet Methoden zur GCD-Berechnung und modularen Arithmetik :

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

Es gibt auch Methoden zur Primgenerierung und zum Testen der Primalität :

@Test public void givenBigIntegers_whenPrimeOperations_thenExpectedResult() { BigInteger i = BigInteger.probablePrime(100, new Random()); boolean isProbablePrime = i.isProbablePrime(1000); assertEquals(true, isProbablePrime); }

7. Fazit

In diesem kurzen Tutorial haben wir die Klassen BigDecimal und BigInteger untersucht. Sie sind nützlich für erweiterte numerische Berechnungen, bei denen die primitiven Ganzzahltypen nicht ausreichen.

Wie immer finden Sie den vollständigen Quellcode auf GitHub.