BigDecimal dan BigInteger di Java

1. Ikhtisar

Dalam tutorial ini, kami akan mendemonstrasikan kelas BigDecimal dan BigInteger .

Kami akan menjelaskan dua tipe data, karakteristiknya, dan skenario penggunaannya. Kami juga akan membahas secara singkat berbagai operasi menggunakan dua kelas.

2. BigDecimal

BigDecimal mewakili angka desimal bertanda tangan presisi arbitrer yang tidak dapat diubah . Ini terdiri dari dua bagian:

  • Nilai tidak berskala - bilangan bulat presisi arbitrer
  • Skala - bilangan bulat 32-bit yang mewakili jumlah digit di sebelah kanan koma desimal

Misalnya, BigDecimal 3,14 memiliki nilai tanpa skala 314 dan skala 2.

Kami menggunakan BigDecimal untuk aritmatika presisi tinggi. Kami juga menggunakannya untuk kalkulasi yang membutuhkan kontrol atas skala dan perilaku pembulatan . Salah satu contohnya adalah perhitungan yang melibatkan transaksi keuangan.

Kita dapat membuat objek BigDecimal dari String , array karakter, int , long , dan 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()); }

Kami juga dapat membuat BigDecimal dari double :

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

Namun, hasilnya dalam hal ini berbeda dari yang diharapkan (yaitu 0,1). Hal ini karena:

  • yang ganda konstruktor tidak terjemahan yang tepat
  • 0,1 tidak memiliki representasi yang tepat dalam dua kali lipat

Oleh karena itu, kita harus menggunakan konstruktor S tring daripada konstruktor ganda .

Selain itu, kita dapat mengonversi double dan long menjadi BigInteger menggunakan metode statis 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()); }

Metode ini mengonversi dua kali lipat menjadi representasi Stringnya sebelum mengonversi ke BigDecimal . Selain itu, dapat menggunakan kembali contoh objek.

Oleh karena itu, kita harus menggunakan metode valueOf dalam preferensi ke konstruktor .

3. Operasi di BigDecimal

Sama seperti kelas Angka lainnya ( Integer , Long , Double dll.), BigDecimal menyediakan operasi untuk operasi aritmatika dan perbandingan. Ini juga menyediakan operasi untuk manipulasi skala, pembulatan dan konversi format.

Itu tidak membebani operator aritmatika (+, -, /, *) atau logika (>. <Etc). Sebagai gantinya, kami menggunakan metode yang sesuai - menambah , mengurangi , mengalikan , membagi dan membandingkanTo.

BigDecimal memiliki metode untuk mengekstrak berbagai atribut, seperti presisi, skala, dan tanda :

@Test public void whenGettingAttributes_thenExpectedResult() { BigDecimal bd = new BigDecimal("-12345.6789"); assertEquals(9, bd.precision()); assertEquals(4, bd.scale()); assertEquals(-1, bd.signum()); }

Kami membandingkan nilai dari dua BigDecimals menggunakan compareTo metode :

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

Metode ini mengabaikan skala saat membandingkan.

Di sisi lain, para sama metode menganggap dua BigDecimal objek sebagai sama hanya jika mereka sama dalam nilai dan skala . Jadi, BigDecimals 1.0 dan 1.00 tidak sama jika dibandingkan dengan metode ini.

@Test public void whenEqualsCalled_thenSizeAndScaleMatched() { BigDecimal bd1 = new BigDecimal("1.0"); BigDecimal bd2 = new BigDecimal("1.00"); assertFalse(bd1.equals(bd2)); }

Kami melakukan operasi aritmatika dengan memanggil metode yang sesuai :

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

Karena BigDecimal tidak dapat diubah, operasi ini tidak mengubah objek yang ada. Sebaliknya, mereka mengembalikan objek baru.

4. Pembulatan dan BigDecimal

Dengan membulatkan angka, kami menggantinya dengan angka lain yang memiliki representasi yang lebih pendek, lebih sederhana dan lebih bermakna . Misalnya, kami membulatkan $ 24,784917 menjadi $ 24,78 karena kami tidak memiliki pecahan sen.

Mode presisi dan pembulatan yang akan digunakan bervariasi tergantung pada perhitungan. Misalnya, pengembalian Pajak Federal AS menentukan pembulatan ke seluruh jumlah dolar menggunakan HALF_UP .

Ada dua kelas yang mengontrol perilaku pembulatan - RoundingMode dan MathContext .

The enum RoundingMode menyediakan delapan mode pembulatan:

  • CEILING - membulatkan ke arah positif tak terhingga
  • LANTAI - membulatkan menuju tak terhingga negatif
  • NAIK - putaran menjauh dari nol
  • BAWAH - putaran menuju nol
  • HALF_UP - pembulatan menuju "tetangga terdekat" kecuali kedua tetangga berjarak sama, dalam hal ini pembulatan ke atas
  • HALF_DOWN - membulatkan ke arah "tetangga terdekat" kecuali kedua tetangga berjarak sama, dalam hal ini dibulatkan ke bawah
  • HALF_EVEN - membulatkan menuju "tetangga terdekat" kecuali kedua tetangga berjarak sama, dalam hal ini, membulatkan ke arah tetangga genap
  • TIDAK PERLU - pembulatan tidak diperlukan dan ArithmeticException dilemparkan jika tidak ada hasil pasti yang memungkinkan

Mode pembulatan HALF_EVEN meminimalkan bias karena operasi pembulatan. Ini sering digunakan. Ini juga dikenal sebagai pembulatan bankir .

MathContext merangkum mode presisi dan pembulatan . Ada beberapa MathContexts standar:

  • DECIMAL32 - presisi 7 digit dan mode pembulatan HALF_EVEN
  • DECIMAL64 - presisi 16 digit dan mode pembulatan HALF_EVEN
  • DECIMAL128 - presisi 34 digit dan mode pembulatan HALF_EVEN
  • UNLIMITED - aritmatika presisi tak terbatas

Dengan menggunakan kelas ini, kita dapat membulatkan angka BigDecimal menggunakan presisi dan perilaku pembulatan yang ditentukan:

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

Sekarang, mari kita periksa konsep pembulatan menggunakan perhitungan sampel.

Mari kita tulis metode untuk menghitung jumlah total yang harus dibayar untuk suatu barang dengan jumlah dan harga satuan. Mari kita juga menerapkan tarif diskon dan tarif pajak penjualan. Kami membulatkan hasil akhir menjadi sen dengan menggunakan metode 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; }

Sekarang, mari kita tulis pengujian unit untuk metode ini:

@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 mewakili bilangan bulat presisi arbitrer yang tidak dapat diubah . Ini mirip dengan tipe integer primitif tetapi memungkinkan nilai besar yang berubah-ubah.

Ini digunakan ketika bilangan bulat yang terlibat lebih besar dari batas tipe panjang . Misalnya, faktorial 50 adalah 30414093201713378043612608166064768844377641568960512000000000000. Nilai ini terlalu besar untuk ditangani oleh tipe data int atau long . Itu hanya dapat disimpan dalam variabel BigInteger .

Ini banyak digunakan dalam aplikasi keamanan dan kriptografi.

Kita dapat membuat BigInteger dari array byte atau 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()); }

Selain itu, kita dapat mengonversi long menjadi BigInteger menggunakan metode statis valueOf :

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

6. Operasi di BigInteger

Mirip dengan int dan long , BigInteger mengimplementasikan semua operasi aritmatika dan logika. Tapi, itu tidak membebani operator.

Ini juga mengimplementasikan metode yang sesuai dari kelas Matematika : abs , min , max , pow , signum .

Kami membandingkan nilai dari dua BigIntegers menggunakan compareTo metode:

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

Kami melakukan operasi aritmatika dengan memanggil metode yang sesuai:

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

Karena BigInteger tidak dapat diubah, operasi ini tidak mengubah objek yang ada. Tidak seperti, int dan long , operasi ini tidak meluap.

BigInteger memiliki operasi bit yang mirip dengan int dan long . Tapi, kita perlu menggunakan metode sebagai pengganti operator:

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

Ini memiliki metode manipulasi bit tambahan :

@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 menyediakan metode untuk komputasi GCD dan aritmatika modular :

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

Ini juga memiliki metode yang terkait dengan generasi utama dan pengujian primalitas :

@Test public void givenBigIntegers_whenPrimeOperations_thenExpectedResult() { BigInteger i = BigInteger.probablePrime(100, new Random()); boolean isProbablePrime = i.isProbablePrime(1000); assertEquals(true, isProbablePrime); }

7. Kesimpulan

Dalam tutorial singkat ini, kita menjelajahi kelas BigDecimal dan BigInteger. Mereka berguna untuk komputasi numerik tingkat lanjut di mana tipe integer primitif tidak mencukupi.

Seperti biasa, kode sumber lengkap dapat ditemukan di GitHub.