Calculos com numeros primitivos  XML
Índice dos Fóruns » Java Básico
Autor Mensagem
bdoweb
Thread.start()
[Avatar]

Membro desde: 31/10/2006 20:38:29
Mensagens: 29
Offline

Olá Pessoal,

bom como todo iniciante há sempre algumas dúvidas.

Bom eu tenho um método que implementa conforme abaixo:



Porém o resultado é uma dizima periódica de 0.19999999999999996 quando deveria me dar 0.2.

Com quais tipos de objetos devo trabalhar para obter números pecisos com Java?

Obrigado
nbluis
GUJ Master
[Avatar]

Membro desde: 27/05/2006 01:31:51
Mensagens: 1531
Localização: Porto Alegre - RS
Offline

java.math.BigDecimal

Luis Eduardo Bohrer

Any fool can write code that a computer can understand. Good programmers write code that humans can understand.
[WWW]
everson_z
Virtual Machine Man
[Avatar]

Membro desde: 05/07/2005 18:12:25
Mensagens: 590
Localização: SP - São Paulo
Offline


Primeiro pensamento POG Existe casos e casos!
Segundo pensamento POG Do meu jeito é mais fácil e rápido!
Primeira merda POG Fazer as merdas que muitos fizeram por defender o primeiro pensamento POG.
Segunda merda POG Limitar o projeto ao escopo inicial.
Terceira merda POG Se basear em absurdos para defender seu desenvolvimento.
Quarta merda POG Não entender que padrão é beneficio e usar o primeiro pensamento POG.
Quinta merda POG Achar um absurdo eu escrever isso.
[WWW]
thingol
Moderador

Membro desde: 29/07/2004 16:10:13
Mensagens: 17543
Offline

Como você deve saber, existe diferença entre "precisão" e "exatidão".
"Precisão" é a quantidade de dígitos, e "exatidão" é quão próximo está o resultado em relação ao resultado "real".

O "double" é preciso no Java com a precisão de 15 dígitos, ou seja, se o número tiver mais de 15 dígitos (0,199.999.999.999.999.96 tem 17 dígitos - não me pergunte porque o Java mostra mais dígitos que a sua precisão), você tem de arredondar para 15 dígitos para ter o resultado mais exato (ou seja, 0,200.000.000.000.000).

Para você fazer contas matematicamente exatas, você não pode usar ponto flutuante, e se for só contas de soma, subtração, multiplicação e divisão, por incrível que pareça você também não pode usar java.lang.BigDecimal; você precisa construir uma clsse que lida com frações a partir de java.lang.BigInteger. Por exemplo, para obter o resultado "exato" de 1 - 3 * (1 / 3), que é 0, não é suficiente usar BigDecimal (como você deve saber, a divisão em BigDecimal de 1/3 gera uma exceção porque esse número gera uma dízima periódica; mas se você limitar a precisão a, por exemplo, 15 casas, então 1 - 3 * (1 / 3) volta o número 0,000.000.000.000.001 que não é zero como você estava esperando se o resultado fosse exato.
[WWW]
nielsen.tekla
Thread.start()
[Avatar]

Membro desde: 16/07/2007 22:41:14
Mensagens: 31
Localização: Natal-RN
Offline

cara substitua double por float dai vai dar certo


float i = 1.2f;

float j = 1;

System.out.println(i-j);

Nielsen C. Damasceno
[Email] [WWW] [Yahoo!] aim icon [MSN] [ICQ]
bdoweb
Thread.start()
[Avatar]

Membro desde: 31/10/2006 20:38:29
Mensagens: 29
Offline

thingol wrote:Como você deve saber, existe diferença entre "precisão" e "exatidão".
"Precisão" é a quantidade de dígitos, e "exatidão" é quão próximo está o resultado em relação ao resultado "real".

O "double" é preciso no Java com a precisão de 15 dígitos, ou seja, se o número tiver mais de 15 dígitos (0,199.999.999.999.999.96 tem 17 dígitos - não me pergunte porque o Java mostra mais dígitos que a sua precisão), você tem de arredondar para 15 dígitos para ter o resultado mais exato (ou seja, 0,200.000.000.000.000).

Para você fazer contas matematicamente exatas, você não pode usar ponto flutuante, e se for só contas de soma, subtração, multiplicação e divisão, por incrível que pareça você também não pode usar java.lang.BigDecimal; você precisa construir uma clsse que lida com frações a partir de java.lang.BigInteger. Por exemplo, para obter o resultado "exato" de 1 - 3 * (1 / 3), que é 0, não é suficiente usar BigDecimal (como você deve saber, a divisão em BigDecimal de 1/3 gera uma exceção porque esse número gera uma dízima periódica; mas se você limitar a precisão a, por exemplo, 15 casas, então 1 - 3 * (1 / 3) volta o número 0,000.000.000.000.001 que não é zero como você estava esperando se o resultado fosse exato.


Nossa cara o java me parece ser um pouco burocratico para números com exatidão(rs) ...
Não entendi muito bem como poderia utilizar o BigInteger você pode me dar um exemplo.

Bom a Sun disponibiliza alguma pacote para tratar de valores monetários ... Pois meu intuito é tratar de valores monetários ...

Muito Obrigado
thingol
Moderador

Membro desde: 29/07/2004 16:10:13
Mensagens: 17543
Offline

Para valores monetários você não precisa de exatidão (por incrível que pareça), só de precisão fixa (2 algarismos depois da vírgula), se você vai lidar só com centavos, ou 4 algarismos, dependendo do tipo de coisa que você vai fazer.

Nesse caso, é necessário saber se você precisa lidar com mais de 15 algarismos significativos (incluindo os centavos).
Se for precisar de mais de 15 algarismos (normalmente é porque você está interfaceando com alguma base que precisa de mais de 15 dígitos), então você precisa do tal BigDecimal, e saber que ele é um pouco chato para usar.
Se precisar de menos, use double mesmo, que é mais fácil e muito mais rápido, e lembre-se de mostrar os dados sempre corretamente formatados.

[WWW]
thingol
Moderador

Membro desde: 29/07/2004 16:10:13
Mensagens: 17543
Offline

Outra coisa chata com double que já vou lhe avisando: dois valores double normalmente não podem ser comparados facilmente.
Por exemplo, 0.0 != 1.0 - 3.0 * (1.0 / 3.0) (é o tal exemplo chato que eu sempre dou).
Você precisa pôr uma tolerância (no seu caso de valores monetários, coisa de meio centavo de tolerância, para evitar ter de fazer análises muito complicadas de erros.)
[WWW]
bdoweb
Thread.start()
[Avatar]

Membro desde: 31/10/2006 20:38:29
Mensagens: 29
Offline

thingol wrote:Para valores monetários você não precisa de exatidão (por incrível que pareça), só de precisão fixa (2 algarismos depois da vírgula), se você vai lidar só com centavos, ou 4 algarismos, dependendo do tipo de coisa que você vai fazer.

Nesse caso, é necessário saber se você precisa lidar com mais de 15 algarismos significativos (incluindo os centavos).
Se for precisar de mais de 15 algarismos (normalmente é porque você está interfaceando com alguma base que precisa de mais de 15 dígitos), então você precisa do tal BigDecimal, e saber que ele é um pouco chato para usar.
Se precisar de menos, use double mesmo, que é mais fácil e muito mais rápido, e lembre-se de mostrar os dados sempre corretamente formatados.



Ahhh certo acho que entendi ... então vou poder usar números primitivos porém terei de formatar o numero igual everson_z escreveu lá em cima.

Muito obrigado
ricardosoares
JavaEvangelist
[Avatar]

Membro desde: 03/12/2004 09:49:13
Mensagens: 318
Localização: São Paulo, SP, Brasil
Offline

Para fins científicos, computação gráfica e afins, esse "detalhe" não passa de um detalhe. Não influi sensivelmente no resultado.

Já se tratando de dinheiro... é nessas horas que damos valor ao Cobol. Para esses fins, é fácil, preciso e confiável.

Em Java não encontrei outra saída senão trabalhar com o java.math.BigDecimal. Convenhamos: é um saco!

Bem, como eu sempre fui adepto da notação polonesa reversa (RPN), criei uma classe para facilitar o uso do BigDecimal com RPN conforme:

em notação algébrica: 1.2 - 1 =

em RPN : 1.2 = 1 -

com a classe RpnCalculator


 Nome do arquivo RpnCalculator.java [Disk] Download
 Descrição RpnCalculator.java
 Tamanho 5 Kbytes
 Baixado:  168 vez(es)


Ricardo Soares
thingol
Moderador

Membro desde: 29/07/2004 16:10:13
Mensagens: 17543
Offline

De fato é mais fácil usar essa classe do Ricardo Soares que usar diretamente BigDecimal porque no caso de BigDecimal é necessário prestar atenção a um monte de coisas chatas. Por exemplo, quando você faz:

BigDecimal bd1 = new BigDecimal (0.2);

você cria um número diferente de

BigDecimal bd2 = new BigDecimal ("0.2");

Se você subtrair um número do outro, vai descobrir que a diferença é 0.000000000000000011102230246251565404236316680908203125, que não é zero como esperado.

[WWW]
sergiotaborda
GUJ Expert
[Avatar]

Membro desde: 22/03/2005 20:57:48
Mensagens: 3433
Offline

Para trabalhar com dinheiro o melhor é usar o padrão Money.
É um classe que faz as vezes de bigdecimal mas usa um long internamente. Todos os calculos são feitos com numeros inteiros e portanto não ha problemas de arredondamento , já que a divisão é tratada de forma especial.
O dinheiro sempre é distribuido em unidades inteiras. Logo, dividir 100 reais por 3 pessoas é dar a cada um 33,33 centavos e ficar com um centado insdistribuivel. Centavos porque é a menor unidade monetária ( só que isso depende de moeda para moeda e se vc for usar moedas diferentes o seu objeto Money tem que ser mais inteligente, mas dá para fazer). A multiplicação tb é especial já que 3 * (1/3) == 3/3
Enfim, se está pensando em dinheiro use o padrão Money. Fica até mais claro que BigDecimal ou qq outra coisa, compare

paga ( BigDecimal valor)
paga (Money valor)


[WWW]
thingol
Moderador

Membro desde: 29/07/2004 16:10:13
Mensagens: 17543
Offline

Se alguém conhece o tipo Currency do VB, vai ver que ele internamente é um long do Java (64 bits) com as contas feitas em centésimos de centavos (4 casas depois da vírgula). Por isso é semelhante ao padrão Money mencionado pelo Sergio Taborda.
[WWW]
ricardosoares
JavaEvangelist
[Avatar]

Membro desde: 03/12/2004 09:49:13
Mensagens: 318
Localização: São Paulo, SP, Brasil
Offline

sergiotaborda wrote:Para trabalhar com dinheiro o melhor é usar o padrão Money.
É um classe que faz as vezes de bigdecimal mas usa um long internamente. Todos os calculos são feitos com numeros inteiros e portanto não ha problemas de arredondamento , já que a divisão é tratada de forma especial.
O dinheiro sempre é distribuido em unidades inteiras. Logo, dividir 100 reais por 3 pessoas é dar a cada um 33,33 centavos e ficar com um centado insdistribuivel. Centavos porque é a menor unidade monetária ( só que isso depende de moeda para moeda e se vc for usar moedas diferentes o seu objeto Money tem que ser mais inteligente, mas dá para fazer). A multiplicação tb é especial já que 3 * (1/3) == 3/3
Enfim, se está pensando em dinheiro use o padrão Money. Fica até mais claro que BigDecimal ou qq outra coisa, compare

paga ( BigDecimal valor)
paga (Money valor)



A única classe chamada Money que encontrei foi uma presente no pacote do JUnit.

É a esta que vc ser refere ? Esta tem os métodos add, subtract e multiply. Falta o divide.
Rola uma no neoframework. Mas não encontrei o apidoc dela. Seria esta?
thingol
Moderador

Membro desde: 29/07/2004 16:10:13
Mensagens: 17543
Offline

Ele está falando em "padrão", não "classe".

É que ele está dizendo, em resumo, que seria interessante que você criasse uma classe Money (ou sei lá que nome você quer dar a ela) que seguisse o padrão Money que ele está descrevendo.
[WWW]
 
Índice dos Fóruns » Java Básico
Ir para:   
Powered by JForum 2.1.8 © JForum Team