public class doublePrecision {
public static void main(String[] args) {
double total = 0;
total += 5.6;
total += 5.8;
System.out.println(total);
}
}
O código acima é impresso:
11.399999999999
Como eu conseguiria isso para imprimir (ou poder usá-lo como) 11.4?
java
floating-point
double
precision
Deinumite
fonte
fonte
Respostas:
Como outros já mencionaram, você provavelmente desejará usar a
BigDecimal
classe, se quiser ter uma representação exata de 11.4.Agora, uma pequena explicação sobre por que isso está acontecendo:
Os tipos
float
edouble
primitivo em Java são números de ponto flutuante , onde o número é armazenado como uma representação binária de uma fração e um expoente.Mais especificamente, um valor de ponto flutuante de precisão dupla, como o
double
tipo, é um valor de 64 bits, em que:Essas partes são combinadas para produzir uma
double
representação de um valor.(Fonte: Wikipedia: precisão dupla )
Para obter uma descrição detalhada de como os valores de ponto flutuante são manipulados em Java, consulte a Seção 4.2.3: Tipos, formatos e valores de ponto flutuante da especificação da linguagem Java.
Os
byte
,char
,int
,long
tipos são ponto fixo de números, que são representions exactas de números. Diferentemente dos números de ponto fixo, os números de ponto flutuante algumas vezes (é seguro assumir "na maioria das vezes") não poderão retornar uma representação exata de um número. Esta é a razão pela qual você acaba11.399999999999
como resultado de5.6 + 5.8
.Ao exigir um valor exato, como 1,5 ou 150.1005, você desejará usar um dos tipos de ponto fixo, que será capaz de representar o número exatamente.
Como já foi mencionado várias vezes, Java possui uma
BigDecimal
classe que manipulará números muito grandes e números muito pequenos.Na Referência da API Java da
BigDecimal
classe:Existem muitas perguntas sobre o Stack Overflow relacionadas à questão dos números de ponto flutuante e sua precisão. Aqui está uma lista de perguntas relacionadas que podem ser interessantes:
Se você realmente quer se aprofundar nos mínimos detalhes dos números de ponto flutuante, dê uma olhada no que todo cientista da computação deve saber sobre aritmética de ponto flutuante .
fonte
BigDecimal
seja muito mais lento do quedouble
neste caso, não é necessário, pois o dobro tem 15 casas decimais de precisão, você só precisa de arredondamentos.Quando você digita um número duplo, por exemplo,
33.33333333333333
o valor que você obtém é, na verdade, o valor mais próximo da precisão dupla representável, exatamente:Dividir isso por 100 dá:
que também não é representável como um número de precisão dupla, então é arredondado novamente para o valor representável mais próximo, que é exatamente:
Quando você imprime esse valor, ele é arredondado novamente para 17 dígitos decimais, fornecendo:
fonte
Se você deseja apenas processar valores como frações, pode criar uma classe Fraction que contém um campo numerador e denominador.
Escreva métodos para adicionar, subtrair, multiplicar e dividir, bem como um método toDouble. Dessa forma, você pode evitar flutuações durante os cálculos.
EDIT: implementação rápida,
fonte
numerator
edenominator
deve serint
s? Por que você deseja precisão de ponto flutuante?Observe que você teria o mesmo problema se usasse a aritmética decimal de precisão limitada e desejasse lidar com 1/3: 0,333333333 * 3 é 0,99999999999, e não 1,0000000000.
Infelizmente, 5.6, 5.8 e 11.4 simplesmente não são números redondos em binário, porque envolvem quintos. Portanto, a representação flutuante deles não é exata, assim como 0,3333 não é exatamente 1/3.
Se todos os números usados forem decimais não recorrentes e você desejar resultados exatos, use BigDecimal. Ou, como outros já disseram, se seus valores são como dinheiro, no sentido de que são todos múltiplos de 0,01 ou 0,001, ou algo assim, multiplique tudo por uma potência fixa de 10 e use int ou long (adição e subtração são trivial: cuidado com a multiplicação).
No entanto, se você está satisfeito com o binário para o cálculo, mas deseja imprimir as coisas em um formato um pouco mais amigável, tente
java.util.Formatter
ouString.format
. No formato string, especifique uma precisão menor que a precisão total de um duplo. Para 10 números significativos, digamos, 11.399999999999 é 11,4, portanto o resultado será quase tão preciso e legível por humanos nos casos em que o resultado binário estiver muito próximo de um valor que requer apenas algumas casas decimais.A precisão a especificar depende um pouco da quantidade de matemática que você fez com seus números - em geral, quanto mais você faz, mais erros se acumulam, mas alguns algoritmos a acumulam muito mais rápido que outros (eles são chamados de "instáveis" como oposta a "estável" em relação a erros de arredondamento). Se tudo o que você está fazendo é adicionar alguns valores, acho que soltar apenas uma casa decimal de precisão resolverá o problema. Experimentar.
fonte
Você pode querer usar a classe java.math.BigDecimal do java, se realmente precisa de matemática de precisão. Aqui está um bom artigo da Oracle / Sun sobre o caso do BigDecimal . Embora você nunca possa representar 1/3 como alguém mencionado, você pode decidir exatamente o quão preciso você deseja que o resultado seja. setScale () é seu amigo .. :)
Ok, porque tenho muito tempo em minhas mãos no momento, aqui está um exemplo de código relacionado à sua pergunta:
e para conectar meu novo idioma favorito, Groovy, aqui está um exemplo mais claro da mesma coisa:
fonte
Certamente você poderia ter transformado isso em um exemplo de três linhas. :)
Se você deseja precisão exata, use BigDecimal. Caso contrário, você pode usar ints multiplicados por 10 ^ qualquer precisão que desejar.
fonte
Como outros observaram, nem todos os valores decimais podem ser representados como binários, pois o decimal é baseado nas potências de 10 e o binário é baseado nas potências de dois.
Se a precisão importa, use BigDecimal, mas se você deseja apenas resultados amigáveis:
Darei à você:
fonte
Você está enfrentando a limitação de precisão do tipo double.
O Java.Math possui alguns recursos aritméticos de precisão arbitrária.
fonte
Você não pode, porque 7.3 não tem uma representação finita em binário. O mais próximo que você pode chegar é 2054767329987789/2 ** 48 = 7,3 + 1/1407374883553280.
Dê uma olhada em http://docs.python.org/tutorial/floatingpoint.html para obter mais explicações. (Está no site do Python, mas Java e C ++ têm o mesmo "problema".)
A solução depende de qual é exatamente o seu problema:
fonte
fonte
Use java.math.BigDecimal
Dobros são frações binárias internamente, portanto, às vezes, não podem representar frações decimais no decimal exato.
fonte
Multiplique tudo por 100 e guarde-o em um centavo.
fonte
Os computadores armazenam números em binário e não podem realmente representar números como 33.333333333 ou 100.0 exatamente. Essa é uma das coisas complicadas sobre o uso de duplas. Você precisará arredondar a resposta antes de mostrá-la ao usuário. Felizmente, na maioria dos aplicativos, você não precisa de tantas casas decimais.
fonte
Os números de ponto flutuante diferem dos números reais, pois para qualquer número de ponto flutuante existe um próximo número de ponto flutuante mais alto. O mesmo que números inteiros. Não há um número inteiro entre 1 e 2.
Não há como representar 1/3 como um flutuador. Há um flutuador abaixo dele e um flutuador acima dele, e há uma certa distância entre eles. E 1/3 está nesse espaço.
O Apfloat for Java afirma trabalhar com números de ponto flutuante de precisão arbitrária, mas nunca o usei. Provavelmente vale a pena dar uma olhada. http://www.apfloat.org/apfloat_java/
Uma pergunta semelhante foi feita aqui antes biblioteca de alta precisão de ponto flutuante Java
fonte
Duplos são aproximações dos números decimais na sua fonte Java. Você está vendo a conseqüência da incompatibilidade entre o dobro (que é um valor com código binário) e sua fonte (que é com código decimal).
Java está produzindo a aproximação binária mais próxima. Você pode usar o java.text.DecimalFormat para exibir um valor decimal com melhor aparência.
fonte
Use um BigDecimal. Ele ainda permite que você especifique regras de arredondamento (como ROUND_HALF_EVEN, que minimizará o erro estatístico ao arredondar para o vizinho par se ambos estiverem na mesma distância; ou seja, 1,5 e 2,5 arredondam para 2).
fonte
Resposta curta: Sempre use BigDecimal e verifique se você está usando o construtor com String argumento , não o duplo.
De volta ao seu exemplo, o código a seguir imprimirá 11.4, conforme desejado.
fonte
Confira BigDecimal, ele lida com problemas ao lidar com aritmética de ponto flutuante assim.
A nova chamada ficaria assim:
Use setScale () para definir o número de precisão da casa decimal a ser usada.
fonte
Por que não usar o método round () da classe Math?
fonte
Se você não tiver outra opção além de usar valores duplos, poderá usar o código abaixo.
fonte
Não desperdice seu esforço usando BigDecimal. Em 99,99999% dos casos, você não precisa disso. O tipo java double é aproximado, mas em quase todos os casos, é suficientemente preciso. Lembre-se de que você tem um erro no 14º dígito significativo. Isso é realmente insignificante!
Para obter uma boa saída, use:
fonte