Quando estamos trabalhando com valores monetários, os tipos float e double não são adequados devido aos problemas de precisão. Nem todas as frações tem representação exata nestes dois tipos de dados.
Para ver isso na prática, basta fazer o seguinte teste:
Altere para float e teste. E terá algo assim: saida = soma 10.000002.
Quando estamos trabalhando com valores monetários precisamos definir as seguintes regras de negócio:
1. Escala - número de casas decimais após a vírgula.
2. Arredondamento - como vamos tratar os valores após a escala definida em 1.
3. Quando arredondar - em que momento vamos fazer o arredondamento.
Para definir essas regras, temos que verificar diversas leis ou normas: fiscais, bancárias, contábeis, legais, etc.
Como float e double não servem temos duas alternativas: ou usar inteiros e controlar o número decimais por conta própria ou usar a classe BigDecimal.
Vamos ver a classe BigDecimal.
Para definir:
BigDecimal bd = new BigDecimal("1.01");
BigDecimal bd2 = new BigDecimal("2.00");
Para somar: bd = bd.add(bd2);
Para subtrair: bd = bd.subtract(bd2);
Para multiplicar: bd = bd.multiply(bd2);
Para dividir: bd = bd.divide(bd2, intScala, BigDecimal.ROUND_UP);
Dicas:
a. Definição
Defina sempre a partir de uma string.
Para converter de float use: Float.toString(meuFloat);
Para converter de double use: Double.toString(meuDouble);
b. Comparação
Use os métodos: compareTo() ou signum() - retorna inteiro (-1 para menor, 0 para == e 1 para maior)
Não use equals() - ele é sensitivo a escala, vai dar diferente se a escala for diferente, mesmo para números iguais.
c. BigDecimal é um objeto imutável
Então após cada operação ele retorna um novo objeto - não se esqueça de salvá-lo!
O BigDecimal tem oito formas de arredondamento.
Segue a lista. Está em inglês, em baixo eu tento decifrar e dou uns exemplos, sempre para a escala 2.
"ROUND_UP " Rounding mode to round away from zero.
Arredonda para: o mais distante de zero, maior em valor absoluto
Se valor negativo: arredonda para o menor valor
Se valor positivo: arredonda para o maior valor
-0.001 => -0.01
0.001 => 0.01
"ROUND_DOWN " Rounding mode to round towards zero.
Arredonda para: o mais próximo de zero, menor em valor absoluto
Se valor negativo: arredonda para o maior valor
Se valor positivo: arredonda para o menor valor
-0.009 => 0.00
0.009 => 0.00
"ROUND_CEILING " Rounding mode to round towards positive infinity.
Arredonda para: o maior valor
-0.001 => 0.00
0.001 => 0.01
"ROUND_FLOOR " Rounding mode to round towards negative infinity.
Arredonda para: o menor valor
-0.001 => -0.01
0.001 => 0.00
"ROUND_HALF_DOWN " Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round down.
Verifica o valor excedente: se diferente de 5, para o "vizinho mais próximo"
se igual a 5 => para o valor absoluto menor
-0.006 => -0.01
-0.005 => 0.00
-0.004 => 0.00
0.004 => 0.00
0.005 => 0.00
0.006 => 0.01
"ROUND_HALF_UP " Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up.
Verifica o valor excedente: se diferente de 5, para o "vizinho mais próximo"
se igual a 5 => para o valor absoluto maior
-0.006 => -0.01
-0.005 => -0.01
-0.004 => 0.00
0.004 => 0.00
0.005 => 0.01
0.006 => 0.01
"ROUND_HALF_EVEN " Rounding mode to round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor.
Verifica o valor excedente: se diferente de 5, para o "vizinho mais próximo"
se igual a 5 => verifica o digito a ser arredondado
se impar => arredonda para o maior em valor absoluto
se par => arredonda para o menor em valor absoluto
Esse tipo de arredondamento é indicado para evitar distorção no resultado devido aos arredondamentos cumulativos na mesma direção. Um uso seria em estatísticas, onde um grande volume de dados são manipulados.
-0.015 => -0.02
-0.025 => -0.02
0.015 => 0.02
0.025 => 0.02
"ROUND_UNNECESSARY" Rounding mode to assert that the requested operation has an exact result, hence no rounding is necessary.
Quando não tem arredondamento a ser efetuado.
Se tiver ocorre um ArithmeticException.
-0.01 => -0.01
0.01 => 0.01
0.011 => ArithmeticException
BigDecimal bd = new BigDecimal("10.001");
bd = bd.setScale(2, BigDecimal.ROUND_UP); // = 10.01
BigAbraços!
Simples e Eficaz
ResponderExcluirDepois de 2 anos não posta nada! Amanha vai ser 11 de setembro..14 anos depois da queda das torre gemeas!
ResponderExcluirNo comments...
ExcluirE hoje que é 6 de novembro 2016? Mas ainda está ajudando... Valeu
ResponderExcluirhoje 15 de novembro de 2017, e continua ajudando....Obrigado!!!
ResponderExcluir