Рубрики
Без рубрики

BigDecimal и BigInteger на Java

Узнайте о типах данных Java BigDecimal и BigInteger.

Автор оригинала: baeldung.

1. Обзор

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

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

2. Большой десятичный

BigDecimal представляет собой неизменяемое десятичное число со знаком произвольной точности . Он состоит из двух частей:

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

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

Мы используем BigDecimal для высокоточной арифметики. Мы также используем его для расчетов, требующих контроля над масштабом и округлением поведения . Одним из таких примеров являются расчеты, связанные с финансовыми операциями.

Мы можем создать BigDecimal объект из Строки , массива символов, 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). Это потому что:

  • конструктор double выполняет точный перевод
  • 0.1 не имеет точного представления в double

Поэтому мы должны использовать конструктор S string вместо double конструктора .

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

Следовательно, мы должны использовать метод valueOf , отдавая предпочтение конструкторам .

3. Операции с большой десятичной

Как и другие Числовые классы ( Целочисленные , Длинные , Двойные и т.д.), 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());
}

Мы сравниваем значение двух больших десятичных чисел с помощью метода 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(bd3.compareTo(bd1) > 0);
    assertTrue(bd1.compareTo(bd2) == 0);
    assertTrue(bd1.compareTo(bd3) <= 0);
    assertTrue(bd1.compareTo(bd2) >= 0);
    assertTrue(bd1.compareTo(bd3) != 0);
}

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

С другой стороны, метод равно рассматривает два больших десятичных объекта как равные, только если они равны по значению и масштабу . Таким образом, 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. Округление и большая десятичная

Округляя число, мы заменяем его другим, имеющим более короткое, простое и осмысленное представление . Например, мы округляем 24,784917 доллара до 24,78 доллара, так как у нас нет дробных центов.

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

Существует два класса, которые управляют поведением округления – Режим округления и MathContext .

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

  • ПОТОЛОК – округляется в сторону положительной бесконечности
  • ЭТАЖ – округляется в сторону отрицательной бесконечности
  • ВВЕРХ – округляется от нуля
  • ВНИЗ – округляется до нуля
  • HALF_UP – округляется в сторону “ближайшего соседа”, если только оба соседа не находятся на одинаковом расстоянии, и в этом случае округляется
  • HALF_DOWN – округляется в сторону “ближайшего соседа”, если только оба соседа не находятся на равном расстоянии, и в этом случае округляется в меньшую сторону
  • HALF_EVEN – округляется в сторону “ближайшего соседа”, если только оба соседа не находятся на равном расстоянии, и в этом случае округляется в сторону четного соседа
  • НЕНУЖНО – округление не требуется и Исключение ArithmeticException выдается, если точный результат невозможен

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

MathContext инкапсулирует как режим точности, так и режим округления . Существует несколько предопределенных Mathcontext:

  • ДЕСЯТИЧНАЯ ДРОБЬ32 – точность 7 цифр и режим округления в ПОЛ-НУЛЯ
  • ДЕСЯТИЧНАЯ ДРОБЬ64 – точность 16 цифр и режим округления в два раза меньше
  • ДЕСЯТИЧНОЕ ЧИСЛО 128 – Точность 34 цифр и режим округления в ПОЛ-НУЛЯ
  • НЕОГРАНИЧЕННАЯ – неограниченная точность арифметики

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

Он используется, когда задействованные целые числа превышают предел типа long . Например, факториал 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 реализует все арифметические и логические операции. Но это не перегружает операторов.

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

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

@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 .