Respostas:
A BigDecimal
é uma maneira exata de representar números. A Double
tem uma certa precisão. Trabalhar com dobras de várias magnitudes (digamos d1=1000.0
e d2=0.001
) pode resultar em uma 0.001
queda total ao somar uma vez que a diferença de magnitude é muito grande. Com BigDecimal
isso não aconteceria.
A desvantagem BigDecimal
é que é mais lento e é um pouco mais difícil programar algoritmos dessa maneira (devido +
-
*
e /
sem sobrecarregar).
Se você está lidando com dinheiro, ou a precisão é uma obrigação, use BigDecimal
. Caso contrário, Doubles
tendem a ser bons o suficiente.
Eu recomendo a leitura do javadoc de BigDecimal
como eles fazem explicar as coisas melhor do que eu aqui :)
if (Math.abs(loadPerServer - maxLoadPerServer) < 0.000001d) {
BigDecimal
", um Double teria mais "precisão" (mais dígitos).
Meu inglês não é bom, então vou escrever um exemplo simples aqui.
double a = 0.02;
double b = 0.03;
double c = b - a;
System.out.println(c);
BigDecimal _a = new BigDecimal("0.02");
BigDecimal _b = new BigDecimal("0.03");
BigDecimal _c = _b.subtract(_a);
System.out.println(_c);
Saída do programa:
0.009999999999999998
0.01
Alguém ainda quer usar o dobro? ;)
System.out.println(0.003f - 0.002f);
BigDecimal é exata:System.out.println(new BigDecimal("0.003").subtract(new BigDecimal("0.002")));
Existem duas diferenças principais em relação ao dobro:
O motivo pelo qual você deve usar o BigDecimal para cálculos monetários não é que ele possa representar qualquer número, mas que ele possa representar todos os números que podem ser representados na noção decimal e que incluem praticamente todos os números do mundo monetário (você nunca transfere 1/3 $ para alguém).
Se você escrever um valor fracionário como 1 / 7
valor decimal, obtém
1/7 = 0.142857142857142857142857142857142857142857...
com uma sequência infinita de 142857
. Como você só pode escrever um número finito de dígitos, inevitavelmente introduzirá um erro de arredondamento (ou truncamento).
Números como 1/10
ou 1/100
expressos como números binários com uma parte fracionária também têm um número infinito de dígitos após o ponto decimal:
1/10 = binary 0.0001100110011001100110011001100110...
Doubles
armazene valores como binários e, portanto, poderá introduzir um erro apenas convertendo um número decimal em um número binário, sem fazer aritmética.
Os números decimais (como BigDecimal
), por outro lado, armazenam cada dígito decimal como está. Isso significa que um tipo decimal não é mais preciso que um ponto flutuante binário ou tipo de ponto fixo em um sentido geral (ou seja, não pode ser armazenado 1/7
sem perda de precisão), mas é mais preciso para números que tenham um número finito de dígitos decimais como é frequentemente o caso dos cálculos de dinheiro.
O Java BigDecimal
tem a vantagem adicional de poder ter um número arbitrário (mas finito) de dígitos em ambos os lados do ponto decimal, limitado apenas pela memória disponível.
BigDecimal é a biblioteca numérica de precisão arbitrária da Oracle. O BigDecimal faz parte da linguagem Java e é útil para uma variedade de aplicativos que variam do financeiro ao científico (é aí que estou).
Não há nada de errado em usar o dobro para determinados cálculos. Suponha, no entanto, que você queira calcular Math.Pi * Math.Pi / 6, ou seja, o valor da função Riemann Zeta para um argumento real de dois (um projeto no qual estou trabalhando atualmente). A divisão de ponto flutuante apresenta um problema doloroso de erro de arredondamento.
O BigDecimal, por outro lado, inclui muitas opções para calcular expressões com precisão arbitrária. Os métodos de adição, multiplicação e divisão, conforme descrito na documentação do Oracle abaixo "substituem", +, * e / no BigDecimal Java World:
http://docs.oracle.com/javase/7/docs/api/java/math/BigDecimal.html
O método compareTo é especialmente útil em loops while e for.
Tenha cuidado, no entanto, ao usar construtores para BigDecimal. O construtor de strings é muito útil em muitos casos. Por exemplo, o código
BigDecimal onethird = new BigDecimal ("0.33333333333");
utiliza uma representação em cadeia de 1/3 para representar esse número de repetição infinita com um grau especificado de precisão. O erro de arredondamento provavelmente está em algum lugar tão profundo na JVM que os erros de arredondamento não perturbarão a maioria dos seus cálculos práticos. Eu, por experiência própria, vi o arredondamento surgir, no entanto. O método setScale é importante nesses aspectos, como pode ser visto na documentação do Oracle.
/* * Portions Copyright IBM Corporation, 2001. All Rights Reserved. */
Se você está lidando com cálculos, existem leis sobre como você deve calcular e qual precisão você deve usar. Se você falhar, estará fazendo algo ilegal. A única razão real é que a representação de bits das casas decimais não é precisa. Como Basil simplesmente coloca, um exemplo é a melhor explicação. Apenas para complementar seu exemplo, eis o que acontece:
static void theDoubleProblem1() {
double d1 = 0.3;
double d2 = 0.2;
System.out.println("Double:\t 0,3 - 0,2 = " + (d1 - d2));
float f1 = 0.3f;
float f2 = 0.2f;
System.out.println("Float:\t 0,3 - 0,2 = " + (f1 - f2));
BigDecimal bd1 = new BigDecimal("0.3");
BigDecimal bd2 = new BigDecimal("0.2");
System.out.println("BigDec:\t 0,3 - 0,2 = " + (bd1.subtract(bd2)));
}
Resultado:
Double: 0,3 - 0,2 = 0.09999999999999998
Float: 0,3 - 0,2 = 0.10000001
BigDec: 0,3 - 0,2 = 0.1
Também temos que:
static void theDoubleProblem2() {
double d1 = 10;
double d2 = 3;
System.out.println("Double:\t 10 / 3 = " + (d1 / d2));
float f1 = 10f;
float f2 = 3f;
System.out.println("Float:\t 10 / 3 = " + (f1 / f2));
// Exception!
BigDecimal bd3 = new BigDecimal("10");
BigDecimal bd4 = new BigDecimal("3");
System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4)));
}
Nos dá a saída:
Double: 10 / 3 = 3.3333333333333335
Float: 10 / 3 = 3.3333333
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion
Mas:
static void theDoubleProblem2() {
BigDecimal bd3 = new BigDecimal("10");
BigDecimal bd4 = new BigDecimal("3");
System.out.println("BigDec:\t 10 / 3 = " + (bd3.divide(bd4, 4, BigDecimal.ROUND_HALF_UP)));
}
Possui a saída:
BigDec: 10 / 3 = 3.3333