BigDecimal e BigInteger em Java
1. Visão geral
Neste tutorial, vamos demonstrarBigDecimal areia as classesBigInteger.
Descreveremos os dois tipos de dados, suas características e cenários de uso. Também cobriremos brevemente as várias operações usando as duas classes.
2. BigDecimal
BigDecimal represents an immutable arbitrary-precision signed decimal number. Consiste em duas partes:
-
Valor não escalado - um número inteiro de precisão arbitrário
-
Escala - um número inteiro de 32 bits que representa o número de dígitos à direita do ponto decimal
Por exemplo,BigDecimal 3,14 tem o valor fora de escala de 314 e a escala de 2.
We use BigDecimal for high-precision arithmetic. We also use it for calculations requiring control over scale and rounding off behavior. Um exemplo é o cálculo envolvendo transações financeiras.
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());
}
Também podemos criarBigDecimal a partir dedouble:
@Test
public void whenBigDecimalCreatedFromDouble_thenValueMayNotMatch() {
BigDecimal bdFromDouble = new BigDecimal(0.1d);
assertNotEquals("0.1", bdFromDouble.toString());
}
No entanto, o resultado, nesse caso, é diferente do esperado (ou seja, 0,1). Isto é porque:
-
o construtordouble faz uma tradução exata
-
0.1 does not have an exact representation in double
Portanto,we should use the String constructor instead of the double constructor.
Além disso, podemos converterdoubleelong emBigInteger usando o método estáticovalueOf:
@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());
}
Este método convertedouble em sua representaçãoString antes de converter emBigDecimal. Além disso, pode reutilizar instâncias de objetos.
Portanto,we should use the valueOf method in preference to the constructors.
3. Operações emBigDecimal
Assim como as outras classesNumber (Integer,Long,Double etc.),BigDecimal fornece operações para operações aritméticas e de comparação. Ele também fornece operações para manipulação de escala, arredondamento e conversão de formato.
Não sobrecarrega a aritmética (+, -, /, *) ou lógica (>. operadores etc. Em vez disso, usamos os métodos correspondentes -add,subtract,multiply,divideecompareTo.
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);
}
Este método ignora a escala durante a comparação.
Por outro lado,the equals method considers two BigDecimal objects as equal only if they are equal in value and scale. Assim,BigDecimals 1,0 e 1,00 não são iguais quando comparados por este método.
@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. Em vez disso, eles retornam novos objetos.
4. Arredondamento eBigDecimal
By rounding a number, we replace it with another having shorter, simpler and more meaningful representation. Por exemplo, arredondamos $ 24,784917 para $ 24,78, pois não temos centavos fracionários.
O modo de precisão e arredondamento a ser usado varia de acordo com o cálculo. Por exemplo, EUA As declarações de impostos federais especificam o arredondamento para valores inteiros em dólares usandoHALF_UP.
There are two classes which control rounding behavior – RoundingMode and MathContext.
Oenum RoundingMode fornece oito modos de arredondamento:
-
CEILING – arredondado em direção ao infinito positivo
-
FLOOR – arredondado para o infinito negativo
-
UP – arredondado para longe de zero
-
DOWN – arredondado para zero
-
HALF_UP – arredondado para "vizinho mais próximo", a menos que ambos os vizinhos sejam equidistantes, caso em que arredondado para cima
-
HALF_DOWN – arredondado para "vizinho mais próximo", a menos que ambos os vizinhos sejam equidistantes, caso em que arredondado para baixo
-
HALF_EVEN – gira em direção ao "vizinho mais próximo", a menos que ambos os vizinhos sejam equidistantes, nesse caso, arredonda em direção ao vizinho par
-
UNNECESSARY – nenhum arredondamento é necessário eArithmeticException é lançado se nenhum resultado exato for possível
O modo de arredondamentoHALF_EVEN minimiza o enviesamento devido às operações de arredondamento. É freqüentemente usado. Também é conhecido comobanker’s rounding.
MathContext encapsulates both precision and rounding mode. Existem alguns MathContexts predefinidos:
-
DECIMAL32 - precisão de 7 dígitos e um modo de arredondamento de HALF_EVEN
-
DECIMAL64 - precisão de 16 dígitos e um modo de arredondamento de HALF_EVEN
-
DECIMAL128 - precisão de 34 dígitos e um modo de arredondamento de HALF_EVEN
-
UNLIMITED - aritmética de precisão ilimitada
Usando esta classe, podemos arredondar um númeroBigDecimal usando a precisão especificada e o comportamento de arredondamento:
@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());
}
Agora, vamos examinar o conceito de arredondamento usando um cálculo de amostra.
Vamos escrever um método para calcular o valor total a ser pago por um item dada uma quantidade e preço unitário. Vamos também aplicar uma taxa de desconto e uma taxa de imposto sobre vendas. Nós arredondamos o resultado final para centavos usando o métodosetScale:
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;
}
Agora, vamos escrever um teste de unidade para este método:
@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. É semelhante aos tipos inteiros primitivos, mas permite valores grandes arbitrários.
It is used when integers involved are larger than the limit of long type. Por exemplo, o fatorial de 50 é30414093201713378043612608166064768844377641568960512000000000000. Este valor é muito grande para ser manipulado por um tipo de dadosint or long. Só pode ser armazenado em uma variávelBigInteger.
É amplamente utilizado em aplicativos de segurança e criptografia.
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());
}
Além disso,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. Operações emBigInteger
Semelhante aint elong,BigInteger implementa todas as operações aritméticas e lógicas. Mas, isso não sobrecarrega os operadores.
Ele também implementa os métodos correspondentes da classeMath:abs,min,max,pow,signum.
Comparamos o valor de dois BigIntegers usando o métodocompareTo:
@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);
}
Realizamos operações aritméticas chamando os métodos correspondentes:
@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);
}
ComoBigInteger é imutável,these operations do not modify the existing objects. Ao contrário,int elong,these operations do not overflow.
BigInteger has the bit operations similar to int and long. Mas, precisamos usar os métodos em vez de operadores:
@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. Conclusão
Neste tutorial rápido, exploramos as classesBigDecimaleBigInteger. . Elas são úteis para cálculos numéricos avançados onde os tipos inteiros primitivos não são suficientes.
Como de costume, o código-fonte completo pode ser encontradoover on GitHub.