Eu li algumas perguntas sobre flaoting números de ponto e sua matemática. Para mim parece que os problemas só acontecem em grandes escalas (ie 10+ decimais). Agora o meu problema acontece em 2 casas decimais já e sua muito grande fora em alguns casos:
Seguinte código:
System.out.println(
String.format("%.2f", BigDecimal.valueOf(2.8).divide(BigDecimal.valueOf(3.87), 2).doubleValue()));
System.out.println(
String.format("%.2f", BigDecimal.valueOf(2.41).divide(BigDecimal.valueOf(2.73), 2).doubleValue()));
gera esta saída respectivamente:
0.80
0.89
Se eu executar os cálculos à mão (usando cálculos do Google), eu recebo estes resultados:
0.72351421188
0.88278388278
para os primeiros cálculos os resultados são muito grande (~ 0,08 desligado) enquanto que para o segundo um seu muito baixo (~ 0,01 desligado).
Existe alguma explicação sensata a respeito de porque thie primeiro resultado é que grande? Ou alguma maneira de obter o resultado correto usando BigDecimal
?
Observe que
System.out.println(2.8/3.87);
realmente retorna o resultado correto (,7235142118863048).
Observe também que o String.format
material só é utilizado para verificar se o resultado foi mudado por ele que não é o caso. BigDecimal.valueOf(2.8).divide(BigDecimal.valueOf(3.87), 2).doubleValue()
produz exatamente o mesmo resultado.
O problema é que você está usando o errado BigDecimal#divide
para o seu arredondamento para 2 casas decimais. Aqui estão os argumentos disponíveis para os BigDecimal#divide
métodos:
divide(BigDecimal divisor)
divide(BigDecimal divisor, int roundingMode)
divide(BigDecimal divisor, MathContext mc)
divide(BigDecimal divisor, RoundingMode roundingMode)
divide(BigDecimal divisor, int scale, int roundingMode)
divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
Desde que você está usando um divide
com um BigDecimal
e int
argumento, portanto, usa o divide(BigDecimal divisor, int roundingMode)
, onde o seu 2
é o modo de arredondamento e não a escala. Neste caso, 2
é, na verdade ROUND_CEILING
, ea escala não é especificado.
Em vez disso, você terá que usar o divide(BigDecimal divisor, int scale, int roundingMode)
ou divide(BigDecimal divisor, int scale, RoundingMode roundingMode)
. Então mude suas chamadas para:
System.out.println(BigDecimal.valueOf(2.8).divide(BigDecimal.valueOf(3.87), 2,
BigDecimal.ROUND_HALF_UP));
System.out.println(BigDecimal.valueOf(2.41).divide(BigDecimal.valueOf(2.73), 2,
BigDecimal.ROUND_HALF_UP));
(Livremente para usar um modo de arredondamento diferente ROUND_HALF_UP
.)
Eu não tenho certeza do que escalá-lo usa por padrão, mas o ROUND_CEILING
que você especificou com os seus 2
causaram os problemas em seus cálculos.
Quanto aos comentários mencionados, existem três maneiras possíveis para criar o BigDecimal
com os valores especificados:
BigDecimal.valueOf(2.8)
new BigDecimal(2.8)
new BigDecimal("2.8")
O new BigDecimal(2.8)
ainda dá erros de ponto flutuante, por isso gostaria de aconselhar para usar BigDecimal.valueOf(2.8)
como você já fez, ou o construtor de Cordas new BigDecimal("2.8")
.
Isso tudo é um pouco irrelevante para os seus problemas de arredondamento no entanto, uma vez usando o correto divide
método vai dar os resultados corretos, independentemente da BigDecimal
inicialização que você usou: