BigDecimal und BigInteger in Java

BigDecimal und BigInteger in Java

1. Überblick

In diesem Tutorial werden wirBigDecimal und die Klassen vonBigIntegerdemonstrieren.

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 represents an immutable arbitrary-precision signed decimal number. Es besteht aus zwei Teilen:

  • Unskalierter Wert - eine Ganzzahl mit beliebiger Genauigkeit

  • Skala - eine 32-Bit-Ganzzahl, die die Anzahl der Stellen rechts vom Dezimalpunkt darstellt

Zum Beispiel hatBigDecimal 3.14 den nicht skalierten Wert von 314 und die Skala von 2.

We use BigDecimal for high-precision arithmetic. We also use it for calculations requiring control over scale and rounding off behavior. Ein Beispiel hierfür sind Berechnungen mit Finanztransaktionen.

We can create a BigDecimal object from String, character array, int, long, and 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());
}

Wir können auchBigDecimal ausdouble erstellen:

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

In diesem Fall ist das Ergebnis jedoch anders als erwartet (dh 0,1). Das ist weil:

  • Der Konstruktordoubleführt eine genaue Übersetzung durch

  • 0.1 does not have an exact representation in double

Daher istwe should use the String constructor instead of the double constructor.

Zusätzlich können wirdouble undlong mit der statischen MethodevalueOf inBigInteger 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 konvertiertdouble in die Darstellung vonString, bevor sie inBigDecimal konvertiert wird. Darüber hinaus können Objektinstanzen wiederverwendet werden.

Daher istwe should use the valueOf method in preference to the constructors.

3. Operationen aufBigDecimal

Genau wie die anderen Klassen vonNumber (Integer,Long,Double usw.) bietetBigDecimal Operationen für Arithmetik- und Vergleichsoperationen. Es bietet auch Funktionen zur Skalenbearbeitung, Rundung und Formatkonvertierung.

Die arithmetischen (+, -, /, *) oder logischen (>) Werte werden nicht überladen. add,subtract,multiply,divide undcompareTo.

BigDecimal has methods to extract various attributes, such as precision, scale, and sign:

@Test
public void whenGettingAttributes_thenExpectedResult() {
    BigDecimal bd = new BigDecimal("-12345.6789");

    assertEquals(9, bd.precision());
    assertEquals(4, bd.scale());
    assertEquals(-1, bd.signum());
}

We compare the value of two BigDecimals using the compareTo method:

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

Diese Methode ignoriert die Skala beim Vergleichen.

Andererseits istthe equals method considers two BigDecimal objects as equal only if they are equal in value and scale. Somit sindBigDecimals 1,0 und 1,00 im Vergleich mit 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));
}

We perform arithmetic operations by calling the corresponding methods:

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

Since BigDecimal is immutable, these operations do not modify the existing objects. Sie geben vielmehr neue Objekte zurück.

4. Rundung undBigDecimal

By rounding a number, we replace it with another having shorter, simpler and more meaningful representation. Zum Beispiel runden wir $ 24.784917 auf $ 24.78, da wir keine Nachkommastellen haben.

Der anzuwendende Genauigkeits- und Rundungsmodus variiert je nach Berechnung. Zum Beispiel USA In der Steuererklärung des Bundes wird angegeben, dass mitHALF_UP auf ganze Dollarbeträge gerundet werden soll.

There are two classes which control rounding behavior – RoundingMode and MathContext.

Dasenum RoundingMode bietet acht Rundungsmodi:

  • CEILING – rundet in Richtung positive Unendlichkeit

  • FLOOR – rundet in Richtung negative Unendlichkeit

  • UP – rundet von Null ab

  • 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

  • UNNECESSARY – ist keine Rundung erforderlich undArithmeticException wird geworfen, wenn kein genaues Ergebnis möglich ist

Der Rundungsmodus vonHALF_EVENminimiert die Vorspannung aufgrund von Rundungsvorgängen. Es wird häufig verwendet. Es ist auch alsbanker’s rounding bekannt.

MathContext encapsulates both precision and rounding mode. Es gibt einige vordefinierte MathContexts:

  • DECIMAL32 - 7-stellige Genauigkeit und ein Rundungsmodus von HALF_EVEN

  • DECIMAL64 - 16-stellige Genauigkeit und ein Rundungsmodus von HALF_EVEN

  • DECIMAL128 - 34 Stellen Genauigkeit und ein Rundungsmodus von HALF_EVEN

  • UNLIMITED - Arithmetik mit unbegrenzter Genauigkeit

Mit dieser Klasse können wir eineBigDecimal-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 MethodesetScaleauf Cent:

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 represents immutable arbitrary-precision integers. Es ähnelt den primitiven Integer-Typen, lässt jedoch beliebig große Werte zu.

It is used when integers involved are larger than the limit of long type. Zum Beispiel ist die Fakultät von 5030414093201713378043612608166064768844377641568960512000000000000.. Dieser Wert ist zu groß, als dass ein Datentyp vonint or longverarbeitet werden könnte. Es kann nur in einerBigInteger-Variablen gespeichert werden.

Es ist weit verbreitet in Sicherheits- und Kryptografieanwendungen.

We can create BigInteger from a byte array or String:

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

Zusätzlichwe can convert a long to BigInteger using the static method*valueOf*:

@Test
public void whenLongConvertedToBigInteger_thenValueMatches() {
    BigInteger bi =  BigInteger.valueOf(2305843009213693951L);

    assertEquals("2305843009213693951", bi.toString());
}

6. Operationen aufBigInteger

Ähnlich wieint undlong implementiertBigInteger alle arithmetischen und logischen Operationen. Die Operatoren werden jedoch nicht überlastet.

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

Wir vergleichen den Wert zweier BigInteger mit der MethodecompareTo:

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

DaBigInteger unveränderlich ist,these operations do not modify the existing objects. Im Gegensatz zuint undlongthese operations do not overflow.

BigInteger has the bit operations similar to int and long. Aber wir müssen 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);
}

It has additional bit manipulation methods:

@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 provides methods for GCD computation and modular arithmetic:

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

It also has methods related to prime generation and primality testing:

@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 KlassenBigDecimal undBigInteger.  untersucht. Sie sind nützlich für erweiterte numerische Berechnungen, bei denen die primitiven Ganzzahltypen nicht ausreichen.

Wie üblich kann der vollständige Quellcodeover on GitHub gefunden werden.