Páginas

segunda-feira, 20 de junho de 2011

Arredondamento de Casas Decimais em Java

Na postagem anterior nós fizemos uma análise do uso da classe java.lang.Math para arredondar números decimais. Uma limitação naqueles métodos estudados é o retorno sempre ser um valor inteiro e nesta postagem nós vamos conhecer outra forma de arredondamento que permite o arredondamento de casas decimais.

java.math.BigDecimal

Esta é a classe em que iremos nos focar nesta postagem.
Esta classe pode trazer diversos benefícios para os programadores Java em contrapartida ao uso dos tipos nativos de ponto flutuante, já que, enquanto nos tipos nativos os valores são representados por proximidade limitada a forma de armazenamento da precisão, os números armazenados através de BigDecimal são representados por exatidão.
E como isso é obtido? A classe armazena duas informações, um número inteiro, e um valor inteiro que representa a escala do nosso número. Por exemplo:
O número decimal 1 é representado pelo número inteiro 1 com escala 0;
O número decimal 1,0 é representado pelo número inteiro 10 com escala 1;
O número decimal 0,1 é representado pelo número inteiro 1 com escala 1;
O número decimal 10 é representado pelo número inteiro 1 com escala -1.
Ou seja, a escala positiva nos indica que devemos deslocar a vírgula para a esquerda, enquanto a escala negativa indica que devemos deslocar a vírgula para a direita.
Então, se você precisa fazer cálculos com valores monetários, e está obtendo valores estranhos com o uso de float ou double, migre seu código para BigDecimal e você deve ter uma grata surpresa.

Mas, o nosso tema não é este

Agora que fomos apresentados a classe que iremos utilizar, vamos analisar as formas de arredondamento que ela disponibiliza.
O método que será utilizado é o setScale(), que pode receber um ou dois parâmetros, e retorna um objeto com valor baseado na operação solicitada sem alterar o valor do objeto original. No nosso caso iremos nos ater a forma de uso com dois parâmetros.

setScale(int newScale, RoundingMode roundingMode)

No primeiro parâmetro passamos a nova escala que desejamos obter, lembrando que o objeto original não é alterado e o resultado esperado está no retorno do método.
O segundo parâmetro define o tipo de arredondamento desejado, vamos listar as opções que utilizaremos fazendo uma breve comparação com os métodos similares da postagem anterior.
RoundingMode.DOWN: não possui equivalente, realiza arredondamento aproximando o resultado de zero. Para números positivos trabalha como o método Math.floor, e para números negativos trabalho como o método Math.ceil;
RoundingMode.FLOOR: equivalente ao método Math.floor;
RoundingMode.HALF_EVEN: equivalente ao método Math.rint;
RoundingMode.HALF_UP: equivalente ao método Math.round;
RoundingMode.HALF_DOWN: equivalente ao método alternativo -Math.round(-d);
RoundingMode.CEILING: equivalente ao método Math.ceil;
RoundingMode.UP: não possui equivalente, realiza arredondamento afastando o resultado de zero. Esta opção é diretamente inversa a opção DOWN, para números positivos trabalha como o método Math.ceil, e para números negativos trabalha como o método Math.floor.

O código

Como código de exemplo vou disponibilizar um código semelhante a postagem anterior para facilitar as comparações entre as formas de arredondamento que conhecemos agora.
import java.math.BigDecimal;
import java.math.RoundingMode;

public class Arredondamentos {
 
 public static void main(String[] args) {
  System.out.println("|   Valor    |   DOWN     |   FLOOR    |  HALF_EVEN |   HALF_UP  |  HALF_DOWN |  CEILING   |     UP     |");
  for(int i = -45; i <= 45; i += 10) {
   for(int j = -1; j <= 1; j++) {
    BigDecimal bigDec = BigDecimal.valueOf(i + j).movePointLeft(3).setScale(4);
    System.out.println(String.format("| %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s |",
                       bigDec, bigDec.setScale(2, RoundingMode.DOWN), bigDec.setScale(2, RoundingMode.FLOOR), bigDec.setScale(2, RoundingMode.HALF_EVEN), bigDec.setScale(2, RoundingMode.HALF_UP), bigDec.setScale(2, RoundingMode.HALF_DOWN), bigDec.setScale(2, RoundingMode.CEILING), bigDec.setScale(2, RoundingMode.UP)));
   }
  }
 }
}

Nenhum comentário:

Postar um comentário

Olá! Antes de postar seu comentário, por favor, observe que comentários técnicos, elogios e sugestões são antecipadamente agradecidos, esclarecimentos sobre os conceitos envolvidos na postagem serão respondidos da melhor forma possível, mas pedidos de ajuda técnica ou suporte individual deverão ser feitos através do formulário de contato. Grato!