Erro somando double

12 respostas
L

sera q alguem pode me ajudar?
estou com o codigo abaixo e por incrivel q pareca ele sempre entra no else.
quando troquei o valor de 59.555,83 para 59.555,82 e para 59.555,84 funcionou blz mas com 59.555,83 nao rola...

JDK : java version 1.4.1_03-b02

alguem sabe pq?

package teste;

import java.math.BigDecimal;

public class Teste {
    
    public static void main(String args[]){
        //259.555,83
        BigDecimal valor1 = new BigDecimal(59555.83);
        BigDecimal valor2 = new BigDecimal(200000);
        double total = 259555.83;
        if(valor1.doubleValue()+valor2.doubleValue() == total){
            System.out.println("Total igual");
        }else{
            System.out.print("Total diferente ");
            System.out.print(valor1.doubleValue()+valor2.doubleValue());
        }        
    }
}

12 Respostas

B

Ola

Olha, eu vi uma comportamento estranho… quando vc soma os dois tem um “2” la no final dos decinais… muito estranho… nao sei te dizer o que seria…
Olha quando vc imprime o resultado… da: 259555.[telefone removido] , donde surgiu este 2 ae num sei nao…

Pode ser alguma manipulacao de bits estranha… com numeros impares… acho que o bit de menor valor significativo… sei la…

Bom, eu dei uma olhada no bug database da sun e nao achei nada relacionado…

Se quizer tentar ver no bug database da sun… o link e http://bugs.sun.com/bugdatabase/index.jsp

B

Ha… esqueci… so pra constar que aqui a JVM e a 1.5.0_07-b03 da sun.

S

Números fracionários não são precisos.
Exemplo: 0,2 em decimal é um número exato.
Em binário, é uma dízima: 0,00110011001100110011…

Qual a finalidade dessa sua comparação?

L

Schuenemann,

o motivo da soma é validação de negocio da aplicação, eu tenho um campo que é composto do somatório de outros, mas eles são abertos para edição, então é preciso validar a “formula”.
Eles estão como BigDecimal pq esse é o mapeamento feito pelo EJB.

hj eu iria testar se na versão JVM 1.5 funcionaria mas o nosso colega BrunoCarlo já fez isso.

eu testei com float e funcionou…

estranhão!

mas com float eu irei perder uma boa parte de representação, e essa aplicação trabalha com valores autos.

L

outro teste q eu fiz foi o seguinte:
usei o metodo Double.compare(double,double)

quando comparei diretamente o método identificou como diferentes mas quando comparei multiplicando por 100 identificou como iguais

System.out.println(Double.compare(total,valor1.doubleValue()+valor2.doubleValue()));
System.out.println(Double.compare(total*100,(valor1.doubleValue()+valor2.doubleValue())*100));

estranhãozão

L

O java não é preciso para calculos com ponto flutuante, uma alternativa para vc (talvez tarde demais se for um sistema legado), é utilizar um valor Long como se fosse valor decimal, por exemplo 10000 representa 1 real (no caso de dinheiro), entao se incluisse 1000 seria equivalente a 10 centavos, 100 a 1 centavo, 10 a 0,1 centavos e 1 para 0,01 centavos… vc trabalha sempre com o long (para calculo) mas quando for mostrar na tela converte para o valor certo ou quando o cara digitasse 1,8931 vc converte para 18931… uma precisão de 4 casas decimais sem ter problemas de arredondamento ou precisão… se quizer maior precisão faz 100000000 = 1 real =P … eu fiz assim aqui (com precisão de 6 casas decimais pelo menos para guardar valores monetários, que acumulando depois podiam me dar diferença de 1 centavo a cada 2 reais processados, ja que meu sistema acumula valores bem pequenos, e que no fim poderia fazer um baita estrago)

Depois quando vc acumular muitos e muitos valores, corria o risco de o calculo ficar errado

C

Pessoas,
Isso não tem NADA a ver com JAVA!
Esse é um problema de toda e qualquer linguagem!
É um problema da computação na verdade.

Mas você consegue resolver o seu problema de soma se vc usar o método add() do BigDecimal.
Nunca faça operações direto com double ou float, utilize sempre os métodos, add,subtract,multiply e etc da classe BigDecimal!

Tenho que certeza que seus problemas irão acabar!

L

clv,

obrigado pela dica mas parece q o problema continua…
eu fiz o seguinte teste

BigDecimal soma = new BigDecimal(0);
        BigDecimal valor1 = new BigDecimal(59555.83);
        BigDecimal valor2 = new BigDecimal(200000);
        BigDecimal esperado = new BigDecimal(259555.83);
        soma = soma.add(valor1);
        soma = soma.add(valor2);
        System.out.println("Soma BigDecimal:="+soma);
        System.out.println("Comparando : "+esperado.compareTo(soma));

e como resultado obtive

Soma BigDecimal:=259555.830000000001746229827404022216796875
Comparando : -1

C

Faz um setScale(2,BigDecimal.ROUND_HALF_UP) ou algum outro tipo de arredondamento.

L

fiz o seguinte teste

BigDecimal soma = new BigDecimal(0);
        BigDecimal valor1 = new BigDecimal(59555.83);
        BigDecimal valor2 = new BigDecimal(200000); 
        valor1.setScale(2,BigDecimal.ROUND_HALF_UP);
        valor2.setScale(2,BigDecimal.ROUND_HALF_UP);
        soma.setScale(2,BigDecimal.ROUND_HALF_UP);
        soma = soma.add(valor1);
        soma = soma.add(valor2);
        BigDecimal esperado = new BigDecimal(259555.83);
        System.out.println("Soma BigDecimal:="+soma);
        System.out.println("Comparando : "+esperado.compareTo(soma));

mas como resultado :

Soma BigDecimal:=259555.830000000001746229827404022216796875
Comparando : -1

eu apelei para o DecimalFormat

DecimalFormat decimalFormat = new DecimalFormat("0.00");
decimalFormat.format(soma).equals(decimalFormat.format(esperado))

alguem tem alguma outra sugestão?

F

O correto para se trabalhar com BigDecimal é passar um objeto String como argumento para o construtor.

Assim:

BigDecimal valor1 = new BigDecimal(59555.83);

BigDecimal valor2 = new BigDecimal(200000);

E depois é só usar as operações aritméticas do BigDecimal add, subtract,multiply,…

Seu exemplo funcionaria dessa forma:

//...
        //259.555,83
        BigDecimal valor1 = new BigDecimal("59555.83");
        BigDecimal valor2 = new BigDecimal("200000");
        double total = 259555.83;
        if(valor1.add(valor2).doubleValue() == total){
            System.out.println("Total igual");
        }else{
            System.out.print("Total diferente ");
            System.out.print(valor1.doubleValue()+valor2.doubleValue());
        }      
//...
D

O problema é justamente que em java os números decimais por default são double, quando vc atribui 59555.83 como número no construtor do BigDecimal, é como se vc estivesse passando um new BigDecimal(double) , ocasionando esse erro de precisão, passando uma String esse problema n acontece.

Criado 22 de agosto de 2006
Ultima resposta 25 de ago. de 2006
Respostas 12
Participantes 7