Arredondando números no Java
Em um sistema desenvolvido para a área de telemarketing de uma empresa foi encontrado um bug na hora de mostrar a média das notas dos atendentes. Temos a seguinte tabela de notas para nos mostrar de forma visual o problema:
Funcionario: Marcelo
Nota: 7,3213832
Funcionario: Yuri
Nota: 6.18317113
Funcionario: Mário
Nota: 9,3181
Note que, nesta tabela todos os valores estão vindo quebrados, com diversas casas decimais. Só que, para apresentar as notas aos funcionários, precisamos simplificar esse valor para que seja melhor visualizado.
Para consertar este bug, primeiro vamos observar o método que calcula e devolve a média:
public static double calculaMedia(Usuario usuario) {
double total = 0.0;
for (Double nota : usuario.getNotas()) {
total += nota;
}
return total / usuario.getNotas().size();
}
Realmente o cálculo da média não toma nenhum cuidado com o arredondamento. O double
resultante terá a quantidade de casas decimais que chegue mais próximo do verdadeiro resultado da operação matemática, respeitando o limite de casas decimais do tipo primitivo double
.
O primeiro passo para resolver esse problema é criar um método que cuida exclusivamente de arredondar o valor calculado pelo método calculaMedia()
. Então, mesmo sem ter criado este método, vamos retorná-lo no calculaMedia()
. Dessa forma:
public static double calculaMedia(Usuario usuario) {
double total = 0.0;
for (Double nota : usuario.getNotas()) {
total += nota;
}
return arredondar(total / usuario.getNotas().size());
}
Criando a função de arredondamento
Já dei spoiler de como será nosso método, ele recebe como parâmetro um double
que é o resultado do calculaMedia
e retorna um double
também, que é o resultado do calculaMedia()
arredondado. Então vamos começar a criar este método:
private static double arredondar(double media) {
}
Para arredondar nosso double
, podemos usar o método estático round() da classe Math:
private static double arredondar(double media) {
return Math.round(media);
}
Tendo como exemplo um funcionário que teve as seguintes notas: 4.0,3.7 e 7.8, representado por este código:
public static void main(String[] args) {
Usuario usuario = new Usuario();
usuario.getNotas().add(4.0);
usuario.getNotas().add(3.7);
usuario.getNotas().add(7.8);
System.out.println(calculaMedia(usuario));
}
A média dele, na calculadora, seria: 5,166666667. Vamos ver o resultado que nosso método nos traz:
Já é uma melhora, pois, pelo menos, não temos aquela infinidade de casas decimais. Mas, o valor real seria 5,166666667 e obtivemos 5. Logo, não está muito preciso, correto?
Isso acontece pois o método round()
devolve o long
mais próximo do valor real passado no parâmetro. No nosso caso, temos aproximadamente o valor real de 5,2. Portanto, o long
mais próximo é o 5.
Esquecendo o 5 que obtivemos vamos tentar de novo com as mesmas notas que usamos no exemplo anterior, mas antes, vamos melhorar nosso método.
Para melhorar esse arredondamento, podemos usar uma gambiarra. Pensando que o método round()
arredonda para o long
mais próximo, podemos multiplicar a media
por 100, usar o round()
e depois dividir por 100. Fazendo isso no código, temos:
private static double arredondar(double media) {
return Math.round(media * 100.0)/100.0;
}
Executando o código, nosso resultado é:
Vamos entender o que aconteceu. Primeiro pegamos nossa media
que no nosso caso tem o valor de 5,166666667 e multiplicamos por 100. Com isso obtivemos 516,6666667, em seguida usamos o round()
que arredondou para o long
mais próximo, 517, e por fim dividimos 517 por 100, obtendo 5,17.
Tudo certo, né? Vamos tentar agora com outros valores. As notas do funcionário agora serão: 3,1, 4,3 e 2,5. O resultado teoricamente seria 3,30. Vamos ver:
Perceba que nosso resultado está correto, mas está com apenas uma casa decimal. Esse é o problema do método que estamos usando para arredondar, não podemos prever quantas casas decimais o resultado terá.
O que seria o ideal? O ideal seria que todas as médias tivessem exatamente duas casas decimais. Para nos ajudar a fazer isso podemos usar a classe DecimalFormat. Essa classe nos ajuda a formatar números para serem exibidos de forma adequada.
Arredondando com DecimalFormat
Para começar, vamos instância-la:
DecimalFormat df = new DecimalFormat("0.00");
"0.00” o que é isso que você colocou?
Vamos entender. Cada 0 significa que o dígito correspondente a aquela casa decimal será mostrado sendo 0 ou não. O próximo passo é pensar em como nosso método irá arredondar. Para isso vamos usar o método setRoundingMode():
df.setRoundingMode(RoundingMode.HALF_UP);
Nessa linha estamos dizendo que arredondaremos da metade para cima, ou seja, caso a média seja 3,625, ela ficará 3,63.
Por fim, vamos formatar nossa média usando o método format()
e já retornar este valor. Dessa forma:
private static String arredondar(double media) {
DecimalFormat df = new DecimalFormat("0.00");
df.setRoundingMode(RoundingMode.HALF_UP);
return df.format(media);
}
Perceba que ao fazer isso recebemos um erro de compilação. Isso aconteceu pois nosso método arredondar()
espera retornar um double
, mas o método format()
devolve uma String
. E agora?
Se pararmos para pensar, até faz sentido retornarmos uma String
, pois já fizemos todas as operações matemáticas que precisamos fazer. Agora só precisamos devolver o valor formatado para o usuário ver. Logo, como não precisaremos mais operar sobre a media
, retornar a mesma como String
faz sentido sim.
Mas não poderíamos apenas transformar essa String em um double e devolver este valor?
Sim, podemos, mas corremos o risco de perder a formatação feita, logo vamos optar por retornar uma String
mesmo. Então, vamos mudar nossos métodos para retornar String
.
Conclusão
Pronto, formatamos os dados da maneira devida. Trabalhar com arredondamento às vezes é uma tarefa complicada, dependendo da situação precisamos de valores extremamente precisos, como, por exemplo, trabalhar com dinheiro.
Para manipular dinheiro o mais adequado é usar a classe BigDecimal que tem um alta precisão.
Para entender melhor como é trabalhar com arredondamentos e principalmente quando o assunto é dinheiro temos o curso da Alura Curso Java Brasil: Formate datas, cpf e números nacionais, onde é usado uma biblioteca externa que ajuda na hora de formatar valores de forma correta.