Eu utilizo o DecimalFormat para fazer o arredondamento de valores em uma aplicação, hoje tive uma situação e me surgiu uma duvida, ao passar um valor de 172,515 o mesmo arredondou o valor para 172,51 quando ao meu ver ele deveria retornar 172,52 uma vez que meu setRoundingMode está definido para HALF_UP, até onde eu tenho conhecimento sempre que usa o HALF_UP valores acima ou igual a 5 ele arredonda para cima, estou correto? senão estiver alguém pode me explicar o porque, obrigado desde já.
Números de ponto flutuante possuem imprecisão por causa da forma como eles são definidos (para entender em detalhes todos os motivos, sugiro fortemente que leia aqui).
Mas apenas para resumir, muitos números não podem ser representados com exatidão em binário, então números como 172.515
são “aproximados” e seu valor real nem sempre é exatamente o mesmo. Então se eu imprimir mais casas decimais dele, poderemos ver o valor que está ali:
float n = 172.515f;
DecimalFormat f = new DecimalFormat("#.##########");
System.out.println(f.format(n)); // 172,5149993896
Veja que na verdade o número é representado como 172,514999...
. Ou seja, o arredondamento está “correto”:
float n = 172.515f;
DecimalFormat f = new DecimalFormat("#.##");
f.setRoundingMode(RoundingMode.HALF_UP);
System.out.println(f.format(n)); // 172,51
Se não quiser perder a precisão, uma alternativa é usar java.math.BigDecimal
:
float n = 172.515f;
BigDecimal b = new BigDecimal(Float.toString(n)).setScale(2, BigDecimal.ROUND_HALF_UP);
System.out.println(b); // 172,52
@hugokotsubo primeiramente obrigado pela resposta
Então pelo o que eu entendi, se eu quiser trabalhar com arredondamento da forma “correta” eu devo usar o BigDecimal ao invés de usar o DecimalFormat, uma vez que esse caso pode acontecer com certa facilidade em aplicações que trabalham com valores, estou correto?
nesse caso, o valor 172,515 é oriundo de uma multiplicação, sendo ela 26,50 * 6,510
vou fazer alguns testes aqui, uma duvida, ao declarar a variável float n = 172.515f , você coloca um f ao final do numero, porque isso?
Por padrão um número com casas decimais é double
, o f
no final força ele para float
. Mas o erro de arredondamento também ocorre com double
, enfim…
E a ideia do BigDecimal
é evitar a perda de precisão que você tem com float
e double
. Os cálculos ficam mais lentos, mas é o preço a se pagar para evitar esses problemas de arredondamento (mas claro que vc deve testar para saber se isso é um problema e se vale a pena).
Ficaria mais ou menos assim:
BigDecimal n1 = new BigDecimal("26.5");
BigDecimal n2 = new BigDecimal("6.51");
BigDecimal n = n1.multiply(n2).setScale(2, BigDecimal.ROUND_HALF_UP);
System.out.println(n); // 172.52
Certo, muito obrigado pela ajuda e pelos esclarecimentos.