BigDecimal dan BigInteger di Java

1. Gambaran keseluruhan

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

Kami akan menerangkan dua jenis data, ciri-ciri mereka, dan senario penggunaannya. Kami juga akan merangkumi secara ringkas pelbagai operasi menggunakan dua kelas tersebut.

2. Perpuluhan Besar

BigDecimal mewakili nombor perpuluhan ditandatangani ketepatan sewenang-wenangnya . Ia terdiri daripada dua bahagian:

  • Nilai tidak berskala - bilangan bulat ketepatan sewenang-wenangnya
  • Skala - bilangan bulat 32-bit yang mewakili bilangan digit di sebelah kanan titik perpuluhan

Sebagai contoh, BigDecimal 3.14 mempunyai nilai tidak berskala 314 dan skala 2.

Kami menggunakan BigDecimal untuk aritmetik berketepatan tinggi. Kami juga menggunakannya untuk pengiraan yang memerlukan kawalan terhadap skala dan kelakuan pembulatan . Salah satu contohnya ialah pengiraan yang melibatkan urus niaga kewangan.

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

Kita juga boleh membuat BigDecimal dari dua kali ganda :

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

Namun, hasilnya, dalam kes ini, berbeza dari yang diharapkan (iaitu 0.1). Ini adalah kerana:

  • yang double pembina melakukan suatu terjemahan yang tepat
  • 0.1 tidak mempunyai perwakilan tepat dalam dua

Oleh itu, kita harus menggunakan konstruktor S tring dan bukannya pembina berganda .

Di samping itu, kita boleh menukar dua kali ganda dan panjang menjadi BigInteger menggunakan kaedah statik 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()); }

Kaedah ini saudara menggandakan kepada yang String perwakilan sebelum memeluk BigDecimal . Di samping itu, ia boleh menggunakan semula objek.

Oleh itu, kita harus menggunakan kaedah valueOf lebih mengutamakan pembina .

3. Operasi di BigDecimal

Sama seperti kelas Nombor yang lain ( Integer , Long , Double etc.), BigDecimal menyediakan operasi untuk operasi aritmetik dan perbandingan. Ini juga menyediakan operasi untuk manipulasi skala, pembundaran dan penukaran format.

Ia tidak membebankan operator aritmetik (+, -, /, *) atau logik (>. <Dll). Sebagai gantinya, kami menggunakan kaedah yang sesuai - tambah , tolak , darab , bahagi dan bandingkanTo.

BigDecimal mempunyai kaedah untuk mengekstrak pelbagai atribut, seperti ketepatan, 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 dua BigDecimals menggunakan kaedah membandingkanTo :

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

Kaedah ini mengabaikan skala semasa membandingkan.

Sebaliknya, yang sama dengan kaedah mempertimbangkan dua BigDecimal objek yang sama hanya jika mereka adalah sama dari segi nilai dan skala . Oleh itu, BigDecimals 1.0 dan 1.00 tidak sama jika dibandingkan dengan kaedah 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 aritmetik dengan memanggil kaedah 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); }

Oleh kerana BigDecimal tidak berubah, operasi ini tidak mengubah objek yang ada. Sebaliknya, mereka mengembalikan objek baru.

4. Pembundaran dan BigDecimal

Dengan membundarkan nombor, kami menggantinya dengan nombor yang lain dengan perwakilan yang lebih pendek, sederhana dan lebih bermakna . Sebagai contoh, kita mengumpul $ 24.784917 kepada $ 24.78 kerana kita tidak mempunyai sen pecahan.

Mod ketepatan dan pembulatan yang akan digunakan berbeza-beza bergantung pada pengiraannya. Sebagai contoh, penyata Cukai Persekutuan AS menetapkan untuk membundarkan kepada jumlah dolar keseluruhan menggunakan HALF_UP .

Terdapat dua kelas yang mengawal tingkah laku pembundaran - RoundingMode dan MathContext .

The enum RoundingMode menyediakan lapan mod pembundaran:

  • CEILING - pusingan ke arah infiniti positif
  • Lantai - pusingan ke arah infiniti negatif
  • UP - pusingan dari sifar
  • TURUN - pusingan ke arah sifar
  • HALF_UP - membelok ke arah "jiran terdekat" kecuali kedua-dua jiran berada sama jarak, dalam hal ini membulatkan
  • HALF_DOWN - membelok ke arah "jiran terdekat" kecuali kedua-dua jiran berada dalam jarak yang sama, dalam hal ini membundarkan
  • HALF_EVEN - membelok ke arah "jiran terdekat" kecuali kedua-dua jiran berada dalam jarak yang sama, dalam hal ini, membelok ke arah jiran yang sama rata
  • TIDAK DIPERLUKAN - pembundaran tidak diperlukan dan Pengecualian Aritmetik dilemparkan jika tidak ada hasil yang tepat

Mod pembulatan HALF_EVEN meminimumkan bias kerana operasi pembundaran. Ia sering digunakan. Ia juga dikenali sebagai pembundaran pegawai bank .

MathContext merangkumi mod ketepatan dan pembundaran . Terdapat beberapa MathContexts yang telah ditetapkan:

  • DECIMAL32 - Ketepatan 7 digit dan mod pembundaran HALF_EVEN
  • DECIMAL64 - ketepatan 16 digit dan mod pembundaran HALF_EVEN
  • DECIMAL128 - Ketepatan 34 digit dan mod pembundaran HALF_EVEN
  • UNLIMITED - aritmetik ketepatan tanpa had

Dengan menggunakan kelas ini, kita dapat membundarkan nombor BigDecimal menggunakan tingkah laku ketepatan dan 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 kaji konsep pembundaran menggunakan contoh pengiraan.

Mari tulis kaedah untuk mengira jumlah yang perlu dibayar untuk item yang diberi kuantiti dan harga seunit. Mari juga menerapkan kadar diskaun dan kadar cukai jualan. Kami membundarkan hasil akhir kepada sen dengan menggunakan kaedah 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 tulis satu ujian unit untuk kaedah 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 ketepatan sewenang-wenang . Ia serupa dengan jenis bilangan bulat primitif tetapi membenarkan nilai besar sewenang-wenangnya.

Ia digunakan apabila bilangan bulat yang terlibat lebih besar daripada had jenis panjang . Contohnya, faktorial 50 adalah 30414093201713378043612608166064768844377641568960512000000000000. Nilai ini terlalu besar untuk dikendalikan oleh jenis data int atau panjang . Ia hanya boleh disimpan dalam pemboleh ubah BigInteger .

Ia digunakan secara meluas dalam aplikasi keselamatan dan kriptografi.

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

Di samping itu, kita boleh menukar lama untuk BigInteger menggunakan statik kaedah valueOf :

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

6. Operasi di BigInteger

Sama seperti int dan panjang , BigInteger melaksanakan semua operasi aritmetik dan logik. Tetapi, ia tidak membebankan operator.

Ia juga menerapkan kaedah yang sesuai dari kelas Matematik : abs , min , max , pow , signum .

Kami membandingkan nilai dua BigIntegers menggunakan kaedah membandingkanTo :

@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 aritmetik dengan memanggil kaedah 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); }

Oleh kerana BigInteger tidak berubah, operasi ini tidak mengubah objek yang ada. Tidak seperti int dan panjang , operasi ini tidak melimpah.

BigInteger mempunyai operasi bit yang serupa dengan int dan panjang . Tetapi, kita perlu menggunakan kaedah dan bukannya pengendali:

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

Ia mempunyai kaedah 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 kaedah untuk pengiraan GCD dan aritmetik 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); }

Ia juga mempunyai kaedah yang berkaitan dengan ujian generasi prima dan primata :

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

7. Kesimpulannya

Dalam tutorial ringkas ini, kami meneroka kelas BigDecimal dan BigInteger. Ia berguna untuk pengiraan berangka lanjutan di mana jenis bilangan bulat primitif tidak mencukupi.

Seperti biasa, kod sumber lengkap boleh didapati di GitHub.