1. 概要

このチュートリアルでは、BigDecimalクラスとBigIntegerクラスを示します。

2つのデータ型、それらの特性、およびそれらの使用シナリオについて説明します。 また、2つのクラスを使用したさまざまな操作についても簡単に説明します。

2. BigDecimal

BigDecimalは、不変の任意精度の符号付き10進数を表します。 これは2つの部分で構成されています。

  • スケーリングされていない値–任意精度の整数
  • スケール–小数点の右側の桁数を表す32ビット整数

たとえば、 BigDecimal 3.14のスケールなしの値は314で、スケールは2です。

高精度の演算にはBigDecimalを使用します。 また、スケールの制御と動作の四捨五入を必要とする計算にも使用します 。  そのような例の1つは、金融取引を含む計算です。

String、文字配列、int、long、およびBigIntegerから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)。 それの訳は:

  • doubleコンストラクターは正確な変換を行います
  • 0.1はdoubleで正確に表現されていません

したがって、doubleコンストラクターの代わりにStringコンストラクターを使用する必要があります。

さらに、 valueOf 静的メソッドを使用して、doubleおよびlongBigDecimalに変換できます。

@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は算術演算と比較演算。 また、スケール操作、丸め、およびフォーマット変換の操作も提供します。

算術(+、-、/、*)または論理(>)をオーバーロードしません。 <など)演算子。 代わりに、対応するメソッド– add 減算乗算除算compareTo。を使用します。

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メソッドを使用して、2つのBigDecimalsの値を比較します。

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

このメソッドは、比較中にスケールを無視します。

一方、 equalsメソッドは、値とscale が等しい場合にのみ、2つのBigDecimalオブジェクトを等しいと見なします。 したがって、この方法で比較した場合、 BigDecimals1.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を使用して全額に四捨五入するように指定しています。

丸め動作を制御する2つのクラス、RoundingModeとMathContextがあります。

列挙型丸めモードは、8つの丸めモードを提供します。

  • CEILING –は正の無限大に向かって丸められます
  • FLOOR –は負の無限大に向かって丸められます
  • UP –はゼロから丸めます
  • DOWN –はゼロに向かって丸めます
  • HALF_UP – は、両方のネイバーが等距離でない限り、「最も近いネイバー」に向かって丸められます。等距離の場合は、切り上げられます。
  • HALF_DOWN – は、両方のネイバーが等距離にある場合を除き、「最も近いネイバー」に向かって丸められます。等距離の場合は、切り捨てられます。
  • HALF_EVEN – 両方のネイバーが等距離でない限り、「最も近いネイバー」に向かって丸められます。等距離の場合は、偶数のネイバーに向かって丸められます。
  • UNNECESSARY – 丸めは不要であり、正確な結果が得られない場合はArithmeticExceptionがスローされます。

HALF_EVEN 丸めモードは、丸め操作によるバイアスを最小限に抑えます。 よく使われます。 バンカーの丸めとも呼ばれます。

MathContextは、精度モードと丸めモードの両方をカプセル化します。  事前定義されたMathContextはほとんどありません。

  • 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は、不変の任意精度の整数を表します。 これはプリミティブ整数型に似ていますが、任意の大きな値を許可します。

関係する整数が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());
}

さらに、静的メソッド valueOf:を使用してlongをBigIntegerに変換できます。

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

6. BigIntegerの操作

intおよびlongと同様に、BigIntegerはすべての算術演算および論理演算を実装します。 ただし、演算子が過負荷になることはありません。

また、 Math クラスの対応するメソッドを実装します: abs min max pow 、[X127X ]signum。

compareTo メソッドを使用して、2つのBigIntegerの値を比較します。

@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にあります。