BigDecimal и BigInteger в Java

BigDecimal и BigInteger в Java

1. обзор

В этом руководстве мы продемонстрируемBigDecimal и классыBigInteger.

Мы опишем два типа данных, их характеристики и сценарии их использования. Мы также кратко рассмотрим различные операции с использованием этих двух классов.

2. BigDecimalс

BigDecimal represents an immutable arbitrary-precision signed decimal number. Он состоит из двух частей:

  • Немасштабированное значение - целое число произвольной точности

  • Масштаб - 32-разрядное целое число, представляющее количество цифр справа от десятичной точки

Например,BigDecimal 3.14 имеет немасштабированное значение 314 и масштаб 2.

We use BigDecimal for high-precision arithmetic. We also use it for calculations requiring control over scale and rounding off behavior. Одним из таких примеров являются расчеты с участием финансовых транзакций.

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

Мы также можем создатьBigDecimal изdouble:

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

Тем не менее, результат, в этом случае, отличается от ожидаемого (то есть 0,1). Это потому что:

  • конструкторdouble выполняет точный перевод

  • 0.1 does not have an exact representation in double

Следовательно,we should use the String constructor instead of the double constructor.

Кроме того, мы можем преобразовать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 в его представлениеString перед преобразованием вBigDecimal. Кроме того, он может повторно использовать экземпляры объекта.

Следовательно,we should use the valueOf method in preference to the constructors.

3. Операции надBigDecimal

Как и другие классыNumber (Integer,Long,Double и т. Д.),BigDecimal предоставляет операции для арифметических операций и операций сравнения. Он также предоставляет операции для манипуляции с масштабом, округления и преобразования формата.

Он не перегружает арифметическое (+, -, /, *) или логическое (>. <и т. д.) операторы. Вместо этого мы используем соответствующие методы -add,subtract,multiply,divide иcompareTo.

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

Этот метод игнорирует масштаб при сравнении.

С другой стороны,the equals method considers two BigDecimal objects as equal only if they are equal in value and scale. Таким образом,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));
}

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. Скорее они возвращают новые объекты.

4. Округление иBigDecimal

By rounding a number, we replace it with another having shorter, simpler and more meaningful representation. Например, мы округляем $ 24,784917 до $ 24,78, поскольку у нас нет дробных центов.

Точность и используемый режим округления зависят от расчета. Например, США В федеральных налоговых декларациях указывается округление до целых долларовых сумм с использованиемHALF_UP.

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

enum RoundingMode обеспечивает восемь режимов округления:

  • CEILING – округляется до положительной бесконечности

  • FLOOR – округляется до отрицательной бесконечности

  • UP – округляет от нуля

  • DOWN – округляется до нуля

  • HALF_UP – округляется к «ближайшему соседу», если оба соседа не равноудалены, в этом случае округляется в большую сторону.

  • HALF_DOWN – округляется до «ближайшего соседа», если оба соседа не равноудалены, в этом случае округляется в меньшую сторону.

  • HALF_EVEN – округляется к «ближайшему соседу», если оба соседа не равноудалены, и в этом случае округляется к четному соседу.

  • UNNECESSARY – округление не требуется, аArithmeticException выбрасывается, если точный результат невозможен

Режим округленияHALF_EVEN сводит к минимуму смещение из-за операций округления. Это часто используется. Он также известен какbanker’s rounding.

MathContext encapsulates both precision and rounding mode. Есть несколько предопределенных MathContexts:

  • DECIMAL32 - точность 7 цифр и режим округления HALF_EVEN

  • DECIMAL64 - 16-значная точность и режим округления HALF_EVEN

  • DECIMAL128 - точность 34 знака и режим округления HALF_EVEN

  • UNLIMITED - арифметика неограниченной точности

Используя этот класс, мы можем округлить число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 represents immutable arbitrary-precision integers. Он похож на примитивные целочисленные типы, но допускает произвольные большие значения.

It is used when integers involved are larger than the limit of long type. Например, факториал 50 равен30414093201713378043612608166064768844377641568960512000000000000. Это значение слишком велико для обработки типом данныхint or long. Его можно сохранить только в переменнойBigInteger.

Он широко используется в приложениях безопасности и криптографии.

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

Кроме того,we 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. Операции надBigInteger

Подобноint иlong,BigInteger реализует все арифметические и логические операции. Но это не перегружает операторов.

Он также реализует соответствующие методы из классаMath:abs,min,max,pow,signum.

Мы сравниваем значение двух BigInteger, используя метод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 является неизменным,these operations do not modify the existing objects. В отличие отint иlong,these operations do not overflow.

BigInteger has the bit operations similar to int and 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);
}

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. Заключение

В этом кратком руководстве мы изучили классыBigDecimal иBigInteger. . Они полезны для сложных численных вычислений, где примитивных целочисленных типов недостаточно.

Как обычно, полный исходный код можно найтиover on GitHub.