Java의 BigDecimal 및 BigInteger

1. 개요

이 튜토리얼에서는 BigDecimalBigInteger 클래스를 보여줍니다 .

두 데이터 유형, 특성 및 사용 시나리오를 설명합니다. 또한 두 클래스를 사용하여 다양한 작업을 간략하게 다룰 것입니다.

2. BigDecimal

BigDecimal 은 변경 불가능한 임의 정밀도의 부호있는 10 진수를 나타냅니다 . 두 부분으로 구성됩니다.

  • 스케일되지 않은 값 – 임의 정밀도 정수
  • Scale – 소수점 오른쪽의 자릿수를 나타내는 32 비트 정수

예를 들어, BigDecimal 3.14의 스케일되지 않은 값은 314이고 스케일은 2입니다.

고정밀 산술을 위해 BigDecimal 을 사용 합니다. 또한 규모를 제어하고 동작을 반올림해야하는 계산에도 사용합니다 . 그러한 예 중 하나는 금융 거래와 관련된 계산입니다.

String , 문자 배열, int , longBigInteger 에서 BigDecimal 객체를 만들 수 있습니다 .

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

double 에서 BigDecimal 을 만들 수도 있습니다 .

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

그러나이 경우 결과는 예상과 다릅니다 (즉, 0.1). 이 때문입니다:

  • 이중 생성자는 정확한 변환을 수행
  • 0.1은 정확한 이중 표현이 없습니다.

따라서 double 생성자 대신 S tring 생성자를 사용해야합니다 .

또한 valueOf 정적 메서드를 사용하여 doublelongBigInteger 로 변환 할 수 있습니다 .

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

이 메소드는 BigDecimal 로 변환하기 전에 doubleString 표현으로 변환합니다 . 또한 객체 인스턴스를 재사용 할 수 있습니다.

따라서 생성자보다 valueOf 메서드를 우선적으로 사용해야합니다 .

3. BigDecimal 에 대한 연산

다른 Number 클래스 ( Integer , Long , Double 등) 와 마찬가지로 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 메서드를 사용하여 두 BigDecimal의 값을 비교합니다 .

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

이 방법은 비교하는 동안 척도를 무시합니다.

한편, 동일 방법은 두 고려 BigDecimal의이 같은 동일 값들이 스케일도 같은 경우에만 개체 . 따라서이 방법으로 비교할 때 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. 반올림 및 BigDecimal

숫자를 반올림하여 더 짧고 단순하며 의미있는 표현을 가진 다른 숫자로 바꿉니다 . 예를 들어, 분수 센트가 없기 때문에 $ 24.784917에서 $ 24.78로 반올림합니다.

사용할 정밀도 및 반올림 모드는 계산에 따라 다릅니다. 예를 들어 미국 연방세 신고는 HALF_UP을 사용하여 전체 달러 금액으로 반올림하도록 지정합니다 .

반올림 동작을 제어하는 ​​클래스는 RoundingModeMathContext 두 가지 입니다.

열거되는 RoundingMode는 여덟 개 반올림 모드를 제공합니다 :

  • CEILING – 양의 무한대로 반올림
  • FLOOR – 음의 무한대로 반올림
  • UP – 0에서 멀어짐
  • DOWN – 0으로 반올림
  • HALF_UP – 두 이웃이 같은 거리에 있지 않으면 "가장 가까운 이웃"으로 반올림합니다.이 경우 반올림됩니다.
  • HALF_DOWN – 두 이웃이 같은 거리에 있지 않으면 "가장 가까운 이웃"으로 반올림합니다.이 경우 반올림됩니다.
  • HALF_EVEN – 두 이웃이 같은 거리에 있지 않으면 "가장 가까운 이웃"쪽으로 반올림합니다.이 경우 짝수 이웃쪽으로 반올림합니다.
  • UNNECESSARY – 반올림이 필요 하지 않으며 정확한 결과가 가능하지 않으면 ArithmeticException 이 발생합니다.

HALF_EVEN 반올림 모드는 반올림 작업으로 인한 편향을 최소화합니다. 자주 사용됩니다. 은행원 반올림 이라고도합니다 .

MathContext encapsulates both precision and rounding mode. There are few predefined MathContexts:

  • DECIMAL32 – 7 digits precision and a rounding mode of HALF_EVEN
  • DECIMAL64 – 16 digits precision and a rounding mode of HALF_EVEN
  • DECIMAL128 – 34 digits precision and a rounding mode of HALF_EVEN
  • UNLIMITED – unlimited precision arithmetic

Using this class, we can round a BigDecimal number using specified precision and rounding behavior:

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

Now, let's examine the rounding concept using a sample calculation.

Let's write a method to calculate the total amount to be paid for an item given a quantity and unit price. Let's also apply a discount rate and sales tax rate. We round the final result to cents by using the setScale method:

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

Now, let's write a unit test for this method:

@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 similar to the primitive integer types but allows arbitrary large values.

It is used when integers involved are larger than the limit of long type. For example, the factorial of 50 is 30414093201713378043612608166064768844377641568960512000000000000. This value is too big for an int or long data type to handle. It can only be stored in a BigInteger variable.

It is widely used in security and cryptography applications.

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

In addition, 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. Operations on BigInteger

Similar to int and long, BigInteger implements all the arithmetic and logical operations. But, it does not overload the operators.

It also implements the corresponding methods from Math class: abs, min, max, pow, signum.

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

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

We perform arithmetic operations by calling the corresponding methods:

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

As BigInteger is immutable, these operations do not modify the existing objects. Unlike, int and long, these operations do not overflow.

BigInteger 에는 intlong유사한 비트 연산이 있습니다. 그러나 연산자 대신 메서드를 사용해야합니다.

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

이 빠른 자습서에서는 BigDecimalBigInteger 클래스를 살펴 보았습니다 . 기본 정수 유형이 충분하지 않은 고급 수치 계산에 유용합니다.

평소처럼 전체 소스 코드는 GitHub에서 찾을 수 있습니다.