Alterar a ordem em uma operação de soma pode produzir resultados diferentes?
1. Visão geral
Neste artigo rápido, veremos por que alterar a ordem da soma retorna um resultado diferente.
2. Problema
Quando olhamos para o código a seguir, podemos prever facilmente a resposta correta (13,22 + 4,88 + 21,45 = 39,55). O que é fácil para nós pode ser interpretado de maneira diferente pelo compilador Java:
double a = 13.22;
double b = 4.88;
double c = 21.45;
double abc = a + b + c;
System.out.println("a + b + c = " + abc); // Outputs: a + b + c = 39.55
double acb = a + c + b;
System.out.println("a + c + b = " + acb); // Outputs: a + c + b = 39.550000000000004
Do ponto de vista matemático, alterar a ordem de uma soma sempre deve dar o mesmo resultado:
(A + B) + C = (A + C) + B
Isso é verdade e funciona bem em Java (e outras linguagens de programação de computadores) para números inteiros. No entanto, quase todas as CPUs usam para números não inteirosIEEE 754 binary floating point standard, o que introduz imprecisão quando o número decimal é armazenado como o valor binário. Os computadores não podem representar todos os números reais com precisão.
Quando alteramos a ordem, também alteramos o valor intermediário armazenado na memória e, portanto, o resultado pode ser diferente. No próximo exemplo, simplesmente começamos com a soma de A + B ou A + C:
double ab = 18.1; // = 13.22 + 4.88
double ac = 34.67; // = 13.22 + 21.45
double sum_ab_c = ab + c;
double sum_ac_b = ac + b;
System.out.println("ab + c = " + sum_ab_c); // Outputs: 39.55
System.out.println("ac + b = " + sum_ac_b); // Outputs: 39.550000000000004
3. Solução
Devido à notória imprecisão dos números de ponto flutuante, o dobro nunca deve ser usado para valores precisos. Isso inclui moeda. Para valores precisos, podemos usar a classeBigDecimal:
BigDecimal d = new BigDecimal(String.valueOf(a));
BigDecimal e = new BigDecimal(String.valueOf(b));
BigDecimal f = new BigDecimal(String.valueOf(c));
BigDecimal def = d.add(e).add(f);
BigDecimal dfe = d.add(f).add(e);
System.out.println("d + e + f = " + def); // Outputs: 39.55
System.out.println("d + f + e = " + dfe); // Outputs: 39.55
Agora podemos ver que nos dois casos os resultados foram os mesmos.
4. Conclusão
Ao trabalhar com valores decimais, sempre precisamos lembrar que os números de ponto flutuante não são representados corretamente e isso pode causar resultados inesperados e indesejados. Quando a precisão é necessária, devemos usar a classeBigDecimal.
Como sempre, o código usado em todo o artigo pode ser encontradoover on GitHub.