Pergunta

Eu tenho que calcular algumas variáveis ​​de ponto flutuante e meu colega me sugere usar BigDecimal em vez de double pois será mais preciso.Mas quero saber o que é e como aproveitar ao máximo BigDecimal?

Foi útil?

Solução

A BigDecimal é uma maneira exata de representar números.A Double tem uma certa precisão.Trabalhando com duplos de várias magnitudes (digamos d1=1000.0 e d2=0.001) poderá resultar na 0.001 sendo descartados completamente ao somar, pois a diferença de magnitude é muito grande.Com BigDecimal isso não aconteceria.

A desvantagem de BigDecimal é que é mais lento e é um pouco mais difícil programar algoritmos dessa maneira (devido a + - * e / não estar sobrecarregado).

Se você está lidando com dinheiro ou se a precisão é obrigatória, use BigDecimal.De outra forma Doubles tendem a ser bons o suficiente.

Eu recomendo a leitura do javadoc de BigDecimal pois eles explicam as coisas melhor do que eu aqui :)

Outras dicas

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 double?;)

Existem duas diferenças principais em relação ao duplo:

  • Precisão arbitrária, semelhante ao BigInteger, eles podem conter número de precisão e tamanho arbitrários
  • Base 10 em vez de Base 2, um BigDecimal é n*10^scale onde n é um número inteiro assinado grande arbitrário e a escala pode ser considerada como o número de dígitos para mover o ponto decimal para a esquerda ou para a direita

A razão pela qual você deve usar BigDecimal para cálculos monetários não é porque ele pode representar qualquer número, mas porque pode 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 como valor decimal você 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 possuem um número infinito de dígitos após a vírgula decimal:

1/10 = binary 0.0001100110011001100110011001100110...

Doubles armazena valores como binários e, portanto, pode introduzir um erro apenas ao converter um número decimal em um número binário, sem sequer fazer qualquer aritmética.

Números decimais (como BigDecimal), por outro lado, armazene cada dígito decimal como está.Isso significa que um tipo decimal não é mais preciso que um tipo binário de ponto flutuante ou de ponto fixo em um sentido geral (ou seja,não pode armazenar 1/7 sem perda de precisão), mas é mais preciso para números que possuem um número finito de dígitos decimais, como é frequentemente o caso para cálculos monetários.

Java BigDecimal tem a vantagem adicional de poder ter um número arbitrário (mas finito) de dígitos em ambos os lados da vírgula decimal, limitado apenas pela memória disponível.

BigDecimal é a biblioteca numérica de precisão arbitrária da Oracle.BigDecimal faz parte da linguagem Java e é útil para uma variedade de aplicações, desde financeiras até científicas (é aí que estou).

Não há nada de errado em usar duplicatas para determinados cálculos.Suponha, entretanto, que você queira calcular Math.Pi * Math.Pi / 6, ou seja, o valor da Função Zeta de Riemann para um argumento real de dois (um projeto no qual estou trabalhando atualmente).A divisão de ponto flutuante apresenta um doloroso problema de erro de arredondamento.

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 da Oracle abaixo, "tomam o lugar" de +, * 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, entretanto, ao usar construtores para BigDecimal.O construtor string é muito útil em muitos casos.Por exemplo, o código

BigDecimal um terço = new BigDecimal("0,33333333333");

utiliza uma representação de string de 1/3 para representar esse número que se repete infinitamente 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 atrapalharão a maioria dos seus cálculos práticos.No entanto, tenho visto, por experiência própria, o arredondamento aumentar.O método setScale é importante nesse aspecto, como pode ser visto na documentação do Oracle.

Se você estiver lidando com cálculo, existem leis sobre como calcular e que precisão deve usar.Se você falhar, estará fazendo algo ilegal.A única razão real é que a representação de bits de casos decimais não é precisa.Como disse Basil, 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)));
}

Saída:

Double:  0,3 - 0,2 = 0.09999999999999998
Float:   0,3 - 0,2 = 0.10000001
BigDec:  0,3 - 0,2 = 0.1

Também temos isso:

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

Tem a saída:

BigDec:  10 / 3 = 3.3333 

Os tipos numéricos primitivos são úteis para armazenar valores únicos na memória.Mas ao lidar com cálculos usando os tipos double e float, há problemas com o arredondamento. Isso acontece porque a representação da memória não mapeia exatamente para o valor.Por exemplo, um valor duplo deve ocupar 64 bits, mas Java não usa todos os 64 bits. Ele apenas armazena o que considera ser as partes importantes do número.Portanto, você pode chegar a valores errados ao adicionar valores do tipo float ou double.Pode ser esse vídeo https://youtu.be/EXxUSz9x7BM vou explicar mais

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top