BigDecimal et BigInteger en Java
1. Vue d'ensemble
Dans ce tutoriel, nous allons démontrerBigDecimal and les classesBigInteger.
Nous allons décrire les deux types de données, leurs caractéristiques et leurs scénarios d'utilisation. Nous couvrirons également brièvement les différentes opérations utilisant les deux classes.
2. BigDecimal
BigDecimal represents an immutable arbitrary-precision signed decimal number. Il se compose de deux parties:
-
Valeur non mise à l'échelle - un entier de précision arbitraire
-
Scale - un entier de 32 bits représentant le nombre de chiffres à droite du point décimal.
Par exemple, leBigDecimal 3.14 a la valeur non mise à l'échelle de 314 et l'échelle de 2.
We use BigDecimal for high-precision arithmetic. We also use it for calculations requiring control over scale and rounding off behavior. Un exemple en est les calculs impliquant des transactions financières.
We can create a BigDecimal object from String, character array, int, long, and 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());
}
On peut aussi créerBigDecimal à partir dedouble:
@Test
public void whenBigDecimalCreatedFromDouble_thenValueMayNotMatch() {
BigDecimal bdFromDouble = new BigDecimal(0.1d);
assertNotEquals("0.1", bdFromDouble.toString());
}
Cependant, le résultat, dans ce cas, est différent de celui attendu (soit 0,1). Ceci est dû au fait:
-
le constructeurdouble effectue une traduction exacte
-
0.1 does not have an exact representation in double
Par conséquent,we should use the String constructor instead of the double constructor.
De plus, nous pouvons convertirdouble etlong enBigInteger en utilisant la méthode statique devalueOf:
@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());
}
Cette méthode convertitdouble en sa représentationString avant de convertir enBigDecimal. En outre, il peut réutiliser des instances d'objet.
Par conséquent,we should use the valueOf method in preference to the constructors.
3. Opérations surBigDecimal
Tout comme les autres classesNumber (Integer,Long,Double etc.),BigDecimal fournit des opérations pour les opérations arithmétiques et de comparaison. Il fournit également des opérations de manipulation de balance, d'arrondissement et de conversion de format.
Il ne surcharge pas l’arithmétique (+, -, /, *) ou logique (>.
BigDecimal has methods to extract various attributes, such as precision, scale, and sign:
@Test
public void whenGettingAttributes_thenExpectedResult() {
BigDecimal bd = new BigDecimal("-12345.6789");
assertEquals(9, bd.precision());
assertEquals(4, bd.scale());
assertEquals(-1, bd.signum());
}
We compare the value of two BigDecimals using the compareTo method:
@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);
}
Cette méthode ignore l'échelle lors de la comparaison.
D'autre part,the equals method considers two BigDecimal objects as equal only if they are equal in value and scale. Ainsi,BigDecimals 1,0 et 1,00 ne sont pas égaux lorsqu'ils sont comparés par cette méthode.
@Test
public void whenEqualsCalled_thenSizeAndScaleMatched() {
BigDecimal bd1 = new BigDecimal("1.0");
BigDecimal bd2 = new BigDecimal("1.00");
assertFalse(bd1.equals(bd2));
}
We perform arithmetic operations by calling the corresponding methods:
@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);
}
Since BigDecimal is immutable, these operations do not modify the existing objects. Au contraire, ils renvoient de nouveaux objets.
4. Arrondi etBigDecimal
By rounding a number, we replace it with another having shorter, simpler and more meaningful representation. Par exemple, nous arrondissons 24,784917 $ à 24,78 $, car nous n’avons pas de centimes.
La précision et le mode d'arrondi à utiliser varient en fonction du calcul. Par exemple, les États-Unis Les déclarations de revenus fédérales spécifient d'arrondir à des montants entiers en dollars en utilisantHALF_UP.
There are two classes which control rounding behavior – RoundingMode and MathContext.
Leenum RoundingMode propose huit modes d'arrondi:
-
CEILING – arrondit à l'infini positif
-
FLOOR – arrondit à l'infini négatif
-
UP – arrondit à zéro
-
DOWN – arrondit vers zéro
-
HALF_UP – arrondit vers le «plus proche voisin» sauf si les deux voisins sont équidistants, auquel cas arrondit vers le haut
-
HALF_DOWN – arrondit vers le «plus proche voisin» sauf si les deux voisins sont équidistants, auquel cas arrondit vers le bas
-
HALF_EVEN – tourne vers le «voisin le plus proche» sauf si les deux voisins sont équidistants, auquel cas, arrondit vers le voisin pair
-
UNNECESSARY – aucun arrondi n'est nécessaire etArithmeticException est lancé si aucun résultat exact n'est possible
Le mode d'arrondi deHALF_EVEN minimise le biais dû aux opérations d'arrondi. Il est fréquemment utilisé. Il est également connu sous le nom debanker’s rounding.
MathContext encapsulates both precision and rounding mode. Il y a peu de MathContexts prédéfinis:
-
DECIMAL32 - précision de 7 chiffres et mode d'arrondi de HALF_EVEN
-
DECIMAL64 - précision de 16 chiffres et mode d'arrondi de HALF_EVEN
-
DECIMAL128 - Précision de 34 chiffres et mode d'arrondi de HALF_EVEN
-
UNLIMITED - arithmétique de précision illimitée
En utilisant cette classe, nous pouvons arrondir un nombreBigDecimal en utilisant la précision et le comportement d'arrondi spécifiés:
@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());
}
Examinons maintenant le concept d'arrondi à l'aide d'un exemple de calcul.
Écrivons une méthode pour calculer le montant total à payer pour un article en fonction d'une quantité et d'un prix unitaire. Appliquons également un taux de remise et un taux de taxe de vente. Nous arrondissons le résultat final à cents en utilisant la méthodesetScale:
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;
}
Maintenant, écrivons un test unitaire pour cette méthode:
@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. Il est similaire aux types entiers primitifs, mais autorise des valeurs arbitraires élevées.
It is used when integers involved are larger than the limit of long type. Par exemple, la factorielle de 50 est30414093201713378043612608166064768844377641568960512000000000000. Cette valeur est trop grande pour un type de donnéesint or long à gérer. Il ne peut être stocké que dans une variableBigInteger.
Il est largement utilisé dans les applications de sécurité et de cryptographie.
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());
}
De plus,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. Opérations surBigInteger
Semblable àint etlong,BigInteger implémente toutes les opérations arithmétiques et logiques. Mais cela ne surcharge pas les opérateurs.
Il implémente également les méthodes correspondantes de la classeMath:abs,min,max,pow,signum.
Nous comparons la valeur de deux BigIntegers en utilisant la méthodecompareTo:
@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);
}
Nous effectuons des opérations arithmétiques en appelant les méthodes correspondantes:
@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);
}
CommeBigInteger est immuable,these operations do not modify the existing objects. Contrairement à,int etlong,these operations do not overflow.
BigInteger has the bit operations similar to int and long. Mais nous devons utiliser les méthodes à la place des opérateurs:
@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);
}
It has additional bit manipulation methods:
@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 provides methods for GCD computation and modular arithmetic:
@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);
}
It also has methods related to prime generation and primality testing:
@Test
public void givenBigIntegers_whenPrimeOperations_thenExpectedResult() {
BigInteger i = BigInteger.probablePrime(100, new Random());
boolean isProbablePrime = i.isProbablePrime(1000);
assertEquals(true, isProbablePrime);
}
7. Conclusion
Dans ce rapide tutoriel, nous avons exploré les classesBigDecimal etBigInteger. Elles sont utiles pour les calculs numériques avancés où les types entiers primitifs ne suffisent pas.
Comme d'habitude, le code source complet peut être trouvéover on GitHub.