Javascript: formatação de um número arredondado para N decimais

104

em JavaScript, a maneira típica de arredondar um número para N casas decimais é algo como:

function roundNumber(num, dec) {
  return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
}

No entanto, essa abordagem arredondará para um máximo de N casas decimais, enquanto eu quero sempre arredondar para N casas decimais. Por exemplo, "2.0" seria arredondado para "2".

Alguma ideia?

hoju
fonte
9
normalmente, você poderia usar toFixed()( developer.mozilla.org/En/Core_JavaScript_1.5_Reference/… ), mas é bugado no IE: stackoverflow.com/questions/661562/… ; você terá que escrever sua própria versão ...
Christoph
1
@hoju - talvez altere a resposta aceita - a resposta de David está correta para o IE8 +, enquanto a resposta aceita tem alguns bugs sérios em todos os navegadores.
robocat
@robocat: Tá falando sério?
Guffa

Respostas:

25

Isso não é um problema de arredondamento, é um problema de exibição. Um número não contém informações sobre dígitos significativos; o valor 2 é igual a 2.0000000000000. É quando você transforma o valor arredondado em uma string que você o faz exibir um certo número de dígitos.

Você pode simplesmente adicionar zeros após o número, algo como:

var s = number.toString();
if (s.indexOf('.') == -1) s += '.';
while (s.length < s.indexOf('.') + 4) s += '0';

(Observe que isso pressupõe que as configurações regionais do cliente usam o ponto como separador decimal, o código precisa de mais trabalho para funcionar para outras configurações.)

Guffa
fonte
3
toFixed é bugado ... por exemplo, o decimal: 1,02449999998 se vc fizer dec.toFixed (4) => 1,0244. Em vez disso, deveria ter sido 1.0245.
Bat_Programmer
2
@deepeshk Mas qual seria o problema em usar toFixed()para preencher decimais no final, após o arredondamento?
pauloya
10
@deepeshk em qual navegador? Apenas tentei no Chrome 17 e 1.02449999998.toFixed(4)retorna corretamente 1.0245.
Matt Ball
3
@MarkTomlin: Então parece que você tem uma string em vez de um número.
Guffa
1
.toFixed () funciona perfeitamente em navegadores modernos (testei o Chrome, Firefox, IE11, IE8). @Bat_Programmer - esclareça quais navegadores você acha que têm esse bug.
robocat
243

Eu acho que há uma abordagem mais simples para todos dados aqui, e é o método Number.toFixed()já implementado em JavaScript.

simplesmente escreva:

var myNumber = 2;

myNumber.toFixed(2); //returns "2.00"
myNumber.toFixed(1); //returns "2.0"

etc ...

David
fonte
Onde é mencionado, hoju? Revisei outras respostas e não encontrei ninguém relatando que a função toFixed tem bugs. Obrigado
David,
@David: Isso é mencionado em um comentário à resposta, por exemplo.
Guffa
13
atenção: toFixed()retorna uma string.
Jordan Arseno
2
@JordanArseno isso pode ser corrigido usando parseInt (myNumber.toFixed (intVar)); para retornar um valor inteiro, ou parseFloat (myNumber.toFixed (floatVar)); para retornar um float se o usuário tiver casas decimais.
Stephen Romero
50

Eu encontrei um caminho. Este é o código de Christoph com uma correção:

function toFixed(value, precision) {
    var precision = precision || 0,
        power = Math.pow(10, precision),
        absValue = Math.abs(Math.round(value * power)),
        result = (value < 0 ? '-' : '') + String(Math.floor(absValue / power));

    if (precision > 0) {
        var fraction = String(absValue % power),
            padding = new Array(Math.max(precision - fraction.length, 0) + 1).join('0');
        result += '.' + padding + fraction;
    }
    return result;
}

Leia os detalhes de repetição de um caractere usando um construtor de array aqui se você estiver curioso para saber por que adicionei o "+ 1".

Elias Zamaria
fonte
Passar um valor de 708.3333333333333 com uma precisão de 2 ou 3 resulta em um valor de retorno de 708,00 para mim. Eu preciso disso para 2 casas decimais, no Chrome e IE 9 .toFixed (2) atendeu às minhas necessidades.
alan de
esta não é uma forma comum, por exemplo, toFixed (16.775, 2) retorna 16,77. Converter o número em String e depois converter é a única maneira.
rodovia
2
Há um bug com este método: toFixed (-0.1111, 2) retorna 0.11, ou seja, o sinal negativo é perdido.
Matt,
Funciona muito bem, exceto por ter adicionado parseFloat (resultado) no final. Vale a pena editar.
Artur Barseghyan
16

Sempre há uma maneira melhor de fazer as coisas.

var number = 51.93999999999761;

Eu gostaria de obter uma precisão de quatro dígitos: 51,94

apenas faça:

number.toPrecision(4);

o resultado será: 51,94

de.la.ru
fonte
2
E se você adicionar 100? Você precisa alterá-lo para number.toPrecision (5)?
JohnnyBizzle de
2
Sim. 31.939383.toPrecision (4)> "31.94" / 131.939383.toPrecision (4)> "
131.9
6

Método de arredondamento semelhante ao PHP

O código a seguir pode ser usado para adicionar sua própria versão de Math.round ao seu próprio namespace, que recebe um parâmetro de precisão. Ao contrário do arredondamento decimal no exemplo acima, ele não executa nenhuma conversão de e para strings, e o parâmetro de precisão funciona da mesma maneira que PHP e Excel, em que um 1 positivo seria arredondado para 1 casa decimal e -1 seria arredondado para dezenas.

var myNamespace = {};
myNamespace.round = function(number, precision) {
    var factor = Math.pow(10, precision);
    var tempNumber = number * factor;
    var roundedTempNumber = Math.round(tempNumber);
    return roundedTempNumber / factor;
};

myNamespace.round(1234.5678, 1); // 1234.6
myNamespace.round(1234.5678, -1); // 1230

da referência do desenvolvedor Mozilla para Math.round ()

JohnnyBizzle
fonte
2

Esperançosamente código funcionando (não fiz muitos testes):

function toFixed(value, precision) {
    var precision = precision || 0,
        neg = value < 0,
        power = Math.pow(10, precision),
        value = Math.round(value * power),
        integral = String((neg ? Math.ceil : Math.floor)(value / power)),
        fraction = String((neg ? -value : value) % power),
        padding = new Array(Math.max(precision - fraction.length, 0) + 1).join('0');

    return precision ? integral + '.' +  padding + fraction : integral;
}
Christoph
fonte
Seu código tem um bug. Tentei corrigir (2.01, 4) e obtive um resultado de "2.100". Se algum dia eu encontrar uma maneira de consertar, vou postar como uma resposta a esta pergunta.
Elias Zamaria
@ mikez302: o cálculo de preenchimento estava errado em um; deve funcionar agora, mas sinta-se à vontade para me incomodar novamente se ainda estiver quebrado ...
Christoph
Muito estranho. Quando executo esta função com o console do firebug aberto no firefox 17, ele congela todo o navegador como se o js fosse pego em um loop infinito. Mesmo que eu não console.log a saída. Se eu não tiver o firebug ativado, o bug não ocorre.
SublymeRick
1
Update, executei no Chrome e recebo: Uncaught RangeError: Tamanho máximo da pilha de chamadas excedido em relação à chamada de função toFixed.
SublymeRick
@SublymeRick: Não tenho ideia de por que isso acontece; tiro no escuro: tente renomear a função ...
Christoph
2

Acho que a função abaixo pode ajudar

function roundOff(value,round) {
   return (parseInt(value * (10 ** (round + 1))) - parseInt(value * (10 ** round)) * 10) > 4 ? (((parseFloat(parseInt((value + parseFloat(1 / (10 ** round))) * (10 ** round))))) / (10 ** round)) : (parseFloat(parseInt(value * (10 ** round))) / ( 10 ** round));
}

uso: roundOff(600.23458,2);irá retornar600.23

KRISHNA TEJA
fonte
2

Isso funciona para arredondar para N dígitos (se você deseja apenas truncar para N dígitos, remova a chamada Math.round e use a chamada Math.trunc):

function roundN(value, digits) {
   var tenToN = 10 ** digits;
   return /*Math.trunc*/(Math.round(value * tenToN)) / tenToN;
}

Tive que recorrer a essa lógica em Java no passado, quando eu estava criando componentes E-Slate de manipulação de dados . Isso porque eu descobri que adicionar 0,1 muitas vezes a 0 você acabaria com alguma parte decimal inesperadamente longa (isso se deve à aritmética de ponto flutuante).

Um comentário do usuário em Formato número para sempre mostrar 2 casas decimais chama essa técnica de escalonamento.

Alguns mencionam que há casos que não são arredondados conforme o esperado e em http://www.jacklmoore.com/notes/rounding-in-javascript/ isso é sugerido:

function round(value, decimals) {
  return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
}
George Birbilis
fonte
btw, POSITS são promissores para arquiteturas h / w futuras: nextplatform.com/2019/07/08/…
George
-2

Se você realmente não se importa com o arredondamento, basta adicionar um toFixed (x) e remover os 0es finais e o ponto, se necessário. Não é uma solução rápida.

function format(value, decimals) {
    if (value) {
        value = value.toFixed(decimals);            
    } else {
        value = "0";
    }
    if (value.indexOf(".") < 0) { value += "."; }
    var dotIdx = value.indexOf(".");
    while (value.length - dotIdx <= decimals) { value += "0"; } // add 0's

    return value;
}
Juan Carrey
fonte
Eu tentei format(1.2, 5)e consegui 1.2, enquanto esperava 1.20000.
Elias Zamaria
Desculpe @EliasZamaria, não entendi no começo. Eu editei a postagem. Observe que ele retornará "0,0000" quando o valor for indefinido.
Juan Carrey