Calculos com numeros primitivos

Olá Pessoal,

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

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

[code] public static void main(String[] args) {
double valor1 = 1.2;
double resultado = valor1 - 1.0;
System.out.println(resultado);

}[/code]

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

java.math.BigDecimal

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.

cara substitua double por float dai vai dar certo

float i = 1.2f;

float j = 1;

System.out.println(i-j);

[quote=thingol]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.
[/quote]

Nossa cara o java me parece ser um pouco burocratico para números com [b]exatidão/b
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

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.

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.)

[quote=thingol]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.

[/quote]

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

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

RpnCalculator rpn = new RpnCalculator(); rpn.enter(1.2d); rpn.subtract(1d); System.out.println(rpn.value());

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.

import java.math.*;

class ProblemasBigDecimal {
    public static void main(String[] args) {
        BigDecimal bd1 = new BigDecimal (0.2);
        BigDecimal bd2 = new BigDecimal ("0.2");
        System.out.println (bd1); // imprime 0.200000000000000011102230246251565404236316680908203125
        System.out.println (bd2); // imprime 0.2
        System.out.println (bd1.subtract (bd2)); // imprime 1.1102230246251565404236316680908203125E-17
    }
}

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)

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.

[quote=sergiotaborda]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)

[/quote]
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?

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.

What Every Computer Scientist Should Know About Floating-Point Arithmetic


	public static void main(String [] args){
		double valor1 = 1.2;  
		double resultado = valor1 - 1.0;
		DecimalFormat f = new DecimalFormat("00.0");
		System.out.println(f.format(resultado));  
	}