Arredondar para N casas decimais em JavaScript de forma Confiável

Dezenas de vezes já precisei arredondar números em JavaScript. É comum buscarmos sempre um Ctrl + C na Internet.

Mas mesmo os procedimentos mais simples podem ter erros sérios de precisão. E é isso que eu busco passar aqui neste site.

Para arredondar um número decimal para 2 casas decimais usando JavaScript, podemos usar Math.round(decimal * 100) / 100, mas este método acaba caindo nas limitações de precisão da linguagem.

Esse é um procedimento padrão e bastante eficaz para arredondarmos um decimal, porém saiba que ele não é um método confiável.

Função pronta para arredondar

Como resposta curta eu tenho pelo menos duas soluções de uma linha, mas infelizmente elas não são nada precisas. E isso se deve à natureza binária de números decimais.

O JavaScript utiliza o padrão IEE 745 para trabalhar com ponto flutuante. Quando uma operação aritmética é realizada em dois números float ou double, é realizado um arredondamento para mais ou para menos na última casa (bit), onde acaba o espaço alocado em memória para representar aquele número.

É por isso que em JavaScript temos resultados bizarros para algumas operações simples, como por exemplo:

const num = 0.2 + 0.4;

// false
console.log(0.6 == num);

// 0.6000000000000001
console.log(num);

Na função nativa de arredondamento Math.round ou no método toFixed também vamos encontrar o mesmo problema ao arredondar para N casas decimais.

Confira abaixo uma função que funciona com precisão, trazida pelo A Kunin

const round = (num, places) => {
	if (!("" + num).includes("e")) {
		return +(Math.round(num + "e+" + places)  + "e-" + places);
	} else {
		let arr = ("" + num).split("e");
		let sig = ""
		if (+arr[1] + places > 0) {
			sig = "+";
		}

		return +(Math.round(+arr[0] + "e" + sig + (+arr[1] + places)) + "e-" + places);
	}
}

console.log(round(1.005, 2)); // 1.01

Nesta função nós usamos a notação exponencial (e+, e-) para trabalhar com o valor decimal. Desta forma, fugimos do problema de precisão explicitado acima.

Mas se você não se importa tanto com as falhas de precisão do JavaScript, pode ficar com esta solução bem mais simples que utiliza uma linha:

const round = (num, places) => {
	return +(parseFloat(num).toFixed(places));
}

console.log(round(1.005, 2)); // 1 - Falha de precisão
console.log(round(1.0051, 2)); // 1.01

A função acima utiliza o método toFixed, que acaba retornando uma string, mas usamos o + na frente que funciona como se fizéssemos Number(num).

Como formatar números decimais

Para formatar números, datas e horários para o padrão usado no Brasil sempre temos que fazer alguma adaptação.

Felizmente em JavaScript existe uma forma muito fácil de se usar vírgulas para casas decimais e pontos como separador milhar:

// Função crua usando padrão Brasileiro
// 1.234,5
(1234.5).toLocaleString('pt-BR');

// Zerofill 2 casas decimais
// 1.234,50
(1234.5).toLocaleString('pt-BR', { minimumFractionDigits: 2 });

// Formato BRL + 2 casas decimais
// R$ 1.234,50
(1234.5).toLocaleString('pt-BR', { minimumFractionDigits: 2, style: 'currency', currency: 'BRL' });

Curiosidade sobre os métodos floor e ceil

Além do método round, temos também os métodos floor e ceil que são comuns à várias outras linguagens.

O que eu queria falar aqui é muito mais um mnemônico para você memorizar estes métodos do que uma curiosidade. É muito mais fácil falar sobre eles explicando antes seus significados em inglês:

  • floor: Chão.
  • ceil (abreviação para ceiling): Teto.

O método estático Math.floor arredonda um número decimal para o "chão". Ele vai retornar o menor valor inteiro derivado do seu argumento. Ex: Math.floor(1.2) = 1.

O método estático Math.ceil arredonda um número decimal para o "teto". Ele vai retornar o maior valor inteiro derivado do seu argumento. Ex: Math.ceil(1.2) = 2.

Este artigo foi útil pra você?

Ricardo Metring

Ricardo Metring

Sou desenvolvedor full stack e co-fundador da Criar.io.
Trabalho há 10 anos com programação e sempre interessado em aprender mais.

Artigos relacionados