Arredonde para no máximo 2 casas decimais (somente se necessário)

2759

Gostaria de arredondar no máximo duas casas decimais, mas apenas se necessário .

Entrada:

10
1.7777777
9.1

Resultado:

10
1.78
9.1

Como posso fazer isso em JavaScript?

stinkycheeseman
fonte
22
Fiz um violino com muitas das técnicas oferecidas como soluções aqui ... assim que você pode comparar: violino
dsdsdsdsd
37
Ninguém parece estar ciente Number.EPSILON. Use Math.round( num * 100 + Number.EPSILON ) / 100.
cronvel
3
Para novos leitores, você não pode fazer isso, a menos que tenha um resultado do tipo string . Matemática de ponto flutuante em representações binárias internas de números significa que sempre existem números que não podem ser representados como decimais simples.
Walf
9
@cronvel Você pode explicar o motivo de usar Number.EPSILONaqui?
18718 Bruce Bruce Sun
5
Caí na toca do coelho e testei algumas das respostas mais interessantes desta página (apenas na primeira página). Aqui está um Codepen . Dica: quanto mais votos positivos a resposta tiver, menores são as chances de funcionar corretamente.
Adam Jagosz 28/08/19

Respostas:

3494

Usar Math.round(num * 100) / 100

Editar: para garantir coisas como 1.005 arredondadas corretamente, usamos

Math.round((num + Number.EPSILON) * 100) / 100

Brian Ustas
fonte
395
Enquanto isso vai funcionar para a maioria dos casos, ele não vai funcionar para 1.005, que vai acabar saindo para ser 1 em vez de 1,01
James
83
@ James Uau, isso é realmente estranho - estou trabalhando no console do desenvolvedor do Chrome e estou notando que 1.005 * 100 = 100.49999999999999. Math.round (100.49999999999999) é avaliado como 100, enquanto Math.round (100.5) é avaliado como 101. O IE9 faz a mesma coisa. Isto é devido ao flutuante estranheza ponto em javascript
stinkycheeseman
153
Uma solução simples. Para 2 dp, use Math.round((num + 0.00001) * 100) / 100. Tente Math.round((1.005 + 0.00001) * 100) / 100eMath.round((1.0049 + 0.00001) * 100) / 100
mrkschan
31
@mrkschan Por que isso funciona e é infalível para todos os números?
precisa saber é o seguinte
86
Para aqueles que não entendem, essa técnica é chamada de escala. Basicamente, o que a resposta faz aqui é trazer dois números pelo ponto decimal, transformando o número em um número inteiro para evitar todos os problemas loucos de ponto flutuante, arredondar isso e depois convertê-lo de volta para o que era antes, dividindo por 100 e você terá o seu responda a 2dp.
Alex_Nabu
3064

Se o valor for um tipo de texto:

parseFloat("123.456").toFixed(2);

Se o valor for um número:

var numb = 123.23454;
numb = numb.toFixed(2);

Há uma desvantagem de que valores como 1,5 fornecerão "1,50" como saída. Uma correção sugerida por @minitech:

var numb = 1.5;
numb = +numb.toFixed(2);
// Note the plus sign that drops any "extra" zeroes at the end.
// It changes the result (which is a string) into a number again (think "0 + foo"),
// which means that it uses only as many digits as necessary.

Parece que Math.roundé uma solução melhor. Mas não é! Em alguns casos, NÃO arredondará corretamente:

Math.round(1.005 * 1000)/1000 // Returns 1 instead of expected 1.01!

O toFixed () também NÃO será arredondado corretamente em alguns casos (testado no Chrome v.55.0.2883.87)!

Exemplos:

parseFloat("1.555").toFixed(2); // Returns 1.55 instead of 1.56.
parseFloat("1.5550").toFixed(2); // Returns 1.55 instead of 1.56.
// However, it will return correct result if you round 1.5551.
parseFloat("1.5551").toFixed(2); // Returns 1.56 as expected.

1.3555.toFixed(3) // Returns 1.355 instead of expected 1.356.
// However, it will return correct result if you round 1.35551.
1.35551.toFixed(2); // Returns 1.36 as expected.

Eu acho que isso ocorre porque o 1.555 é na verdade algo como o float 1.55499994 nos bastidores.

A solução 1 é usar um script com o algoritmo de arredondamento necessário, por exemplo:

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

https://plnkr.co/edit/uau8BlS1cqbvWPCHJeOy?p=preview

NOTA: Esta não é uma solução universal para todos. Existem vários algoritmos de arredondamento diferentes, sua implementação pode ser diferente, depende de seus requisitos. https://en.wikipedia.org/wiki/Rounding

A solução 2 é evitar cálculos de front-end e extrair valores arredondados do servidor back-end.

A Kunin
fonte
81
Essa abordagem (a toFixed) é boa e funcionou para mim, mas especificamente não atende à solicitação original de "somente quando necessário". (Ela arredonda 1,5-1,50, que rompe a especificação.)
Por Lundberg
29
Para o requisito "quando necessário", faça o seguinte: parseFloat(number.toFixed(decimalPlaces)); @PerLundberg
Onur Yıldırım
36
parseFloat("55.555").toFixed(2)retorna "55.55"no console do desenvolvedor do Chrome.
Levi Botelho
22
Não há vantagem em usar toFixed em vez de Math.round; O toFixed leva aos mesmos problemas de arredondamento (experimente-os com 5.555 e 1.005), mas é 500x (sem brincadeira) mais lento que Math.round ... Parece que a resposta @MarkG é a mais precisa aqui.
315 Pierre
17
toFixed "às vezes" não retorna uma string, sempre retorna uma string.
McGuireV10
464

Você pode usar

function roundToTwo(num) {    
    return +(Math.round(num + "e+2")  + "e-2");
}

Eu encontrei isso no MDN . O caminho deles evita o problema com 1.005 mencionado .

roundToTwo(1.005)
1.01
roundToTwo(10)
10
roundToTwo(1.7777777)
1.78
roundToTwo(9.1)
9.1
roundToTwo(1234.5678)
1234.57
MarkG
fonte
13
@Redsandro, +(val)é o equivalente de coerção do uso Number(val). Concatenar "e-2" a um número resultou em uma sequência que precisava ser convertida novamente em um número.
Jack
41
Lembre-se de que para carros alegóricos grandes e minúsculos que produziriam NaN, pois, por exemplo, + "1e-21 + 2" não serão analisados ​​corretamente.
315 Pierre
16
Você 'resolveu' o 'problema' da 1.005, mas introduziu um novo: agora, no console do Chrome, roundToTwo(1.0049999999999999)sai como 1,01 (inevitavelmente, desde então 1.0049999999999999 == 1.005). Parece-me que o float que você obtém se digitar num = 1.005'obviamente' deve 'arredondar para 1,00, porque o valor exato de num é menor que 1,005. Obviamente, também me parece que a string '1.005' 'obviamente' 'deve ser arredondada para 1.01. O fato de pessoas diferentes parecerem ter intuições diferentes sobre qual é o comportamento correto real aqui faz parte do motivo pelo qual é complicado.
Mark Amery
35
Não há número (ponto flutuante) no meio 1.0049999999999999e 1.005, portanto, por definição, eles são o mesmo número. Isso é chamado de corte desagradável.
Azmisov
6
@Azmisov está certo. Enquanto 1.00499 < 1.005estiver true, 1.0049999999999999 < 1.005avalia como false.
falconepl
146

A resposta de MarkG é a correta. Aqui está uma extensão genérica para qualquer número de casas decimais.

Number.prototype.round = function(places) {
  return +(Math.round(this + "e+" + places)  + "e-" + places);
}

Uso:

var n = 1.7777;    
n.round(2); // 1.78

Teste de unidade:

it.only('should round floats to 2 places', function() {

  var cases = [
    { n: 10,      e: 10,    p:2 },
    { n: 1.7777,  e: 1.78,  p:2 },
    { n: 1.005,   e: 1.01,  p:2 },
    { n: 1.005,   e: 1,     p:0 },
    { n: 1.77777, e: 1.8,   p:1 }
  ]

  cases.forEach(function(testCase) {
    var r = testCase.n.round(testCase.p);
    assert.equal(r, testCase.e, 'didn\'t get right number');
  });
})
Lavamantis
fonte
20
Pierre levantou um problema sério com a resposta de MarkG.
dsjoerg
9
Nota: Se você não quiser alterar o Number.prototype - basta escrever isso como uma função: function round(number, decimals) { return +(Math.round(number + "e+" + decimals) + "e-" + decimals); }
Philipp Tsipman
2
Uma maneira legal de expandir isso. Você pode verificar se o decimal é negativo e inverter e + e e-. Então n = 115; n. ao redor (-2); produziria 100
Lee Louviere 23/03
3
Tente n: 1e + 19 - ele retorna NaN #
10777 DavidJ
3
Esse algoritmo sempre arredonda para cima (em vez de se afastar de zero). Assim, no caso de números negativos, a saída não é o que você poderia esperar:(-1.005).round(2) === -1
Aleksej Komarov
123

Você deveria usar:

Math.round( num * 100 + Number.EPSILON ) / 100

Ninguém parece estar ciente de Number.EPSILON .

Também vale a pena notar que isso não é uma esquisitice do JavaScript, como algumas pessoas declararam.

É assim que os números de ponto flutuante funcionam em um computador. Como 99% das linguagens de programação, o JavaScript não possui números de ponto flutuante caseiros ; depende da CPU / FPU para isso. Um computador usa binário e, em binário, não há números como0.1 , mas uma mera aproximação binária para isso. Por quê? Pela mesma razão que 1/3 não pode ser escrito em decimal: seu valor é 0,33333333 ... com um infinito de três.

Aqui vem Number.EPSILON. Esse número é a diferença entre 1 e o próximo número existente nos números de ponto flutuante de precisão dupla. É isso: não há número entre 1e 1 + Number.EPSILON.

EDITAR:

Conforme solicitado nos comentários, vamos esclarecer uma coisa: adicionar Number.EPSILON é relevante apenas quando o valor a arredondar é o resultado de uma operação aritmética, pois pode engolir algum delta de erro de ponto flutuante.

Não é útil quando o valor vem de uma fonte direta (por exemplo: literal, entrada do usuário ou sensor).

EDIT (2019):

Como o @maganap e algumas pessoas apontaram, é melhor adicionar Number.EPSILONantes de multiplicar:

Math.round( ( num + Number.EPSILON ) * 100 ) / 100

EDIT (dezembro de 2019):

Ultimamente, uso uma função semelhante a esta para comparar números com reconhecimento de epsilon:

const ESPILON_RATE = 1 + Number.EPSILON ;
const ESPILON_ZERO = Number.MIN_VALUE ;

function epsilonEquals( a , b ) {
  if ( Number.isNaN( a ) || Number.isNaN( b ) ) {
    return false ;
  }
  if ( a === 0 || b === 0 ) {
    return a <= b + EPSILON_ZERO && b <= a + EPSILON_ZERO ;
  }
  return a <= b * EPSILON_RATE && b <= a * EPSILON_RATE ;
}

Meu caso de uso é uma afirmação + validação de dados que estou desenvolvendo há muitos anos.

De fato, no código que estou usando ESPILON_RATE = 1 + 4 * Number.EPSILONe EPSILON_ZERO = 4 * Number.MIN_VALUE(quatro vezes o epsilon), porque quero um verificador de igualdade solto o suficiente para acumular erro de ponto flutuante.

Até agora, parece perfeito para mim. Eu espero que isso ajude.

cronvel
fonte
1
@palota você poderia definir um número realmente tine como o cronvel EPSILON está mencionando
Daniel San
3
Esta é a resposta certa! O próprio problema está relacionado com a forma como os números flutuantes funcionam internamente, e não sobre javascript
Daniel San
22
Na verdade, você deve adicionar o epsilon ANTES da multiplicação por 100 Math.round( (num + Number.EPSILON) * 100) / 100.. Concordo também que este é o método certo para arredondar corretamente (embora não seja exatamente o que foi solicitado nesta pergunta).
Maganap
2
@cronvel Hmm. Você está certo; a escolha da direção faz sentido. Portanto, acho que a minha objeção restante é apenas que isso só faz sentido se você estiver trabalhando em um domínio em que tenha uma razão de princípios para pensar que seu valor é um "número redondo". Se você sabe que sua entrada é o resultado de operações aritméticas simples em números com pequenos números de casas decimais, com certeza, provavelmente você tem apenas 0.004999999999999999o resultado de um erro de ponto flutuante composto e o resultado matematicamente correto provavelmente foi de 0,005. Se é uma leitura de um sensor? Não muito.
Mark Amery
1
@marchaos Ele não falha: metades são sempre arredondados para cima . Por exemplo Math.round(1.5)= 2, mas Math.round(-1.5)= -1. Portanto, isso é perfeitamente consistente. Aqui -1 é maior que -2, assim como -1000 é maior que -1000,01. Não deve ser confundido com números absolutos maiores.
cronvel 27/03
84

Pode-se usar .toFixed(NumberOfDecimalPlaces).

var str = 10.234.toFixed(2); // => '10.23'
var number = Number(str); // => 10.23
Gourav Singla
fonte
4
Também porque adiciona zeros à direita, o que não é o que a pergunta original pediu .
Alastair Maw
2
Mas zeros à direita são facilmente removidos com um regex, ou seja, `Number (10.10000.toFixed (2) .replace (/ 0 + $ /, ''))` => 10.1
Chad
1
@ Daniel a resposta superior é o mesmo (a partir de agora), mas ele não alsways rodada corretamente, tente +(1.005).toFixed(2)que retorna 1ao invés de 1.01.
Emile Bergeron
3
@ChadMcElligott: Seu regex não funciona bem com números inteiros: Number(9).toFixed(2).replace(/0+$/, '')=> "9."
Jacob van Lingen
Parece que isso não acontece. É apenas trunca basicamente. Em alguns casos, o suficiente! Obrigado.
Iedmrc 21/09/19
78

Esta questão é complicada.

Suponha que tenhamos uma função, roundTo2DP(num)que aceita float como argumento e retorna um valor arredondado para 2 casas decimais. O que cada uma dessas expressões deve avaliar?

  • roundTo2DP(0.014999999999999999)
  • roundTo2DP(0.0150000000000000001)
  • roundTo2DP(0.015)

A resposta 'óbvia' é que o primeiro exemplo deve arredondar para 0,01 (porque é mais próximo de 0,01 do que 0,02), enquanto os outros dois devem arredondar para 0,02 (porque 0,01500000000000000000001 está mais próximo de 0,02 do que 0,01 e porque 0,015 está exatamente na metade do caminho eles e existe uma convenção matemática de que esses números são arredondados).

O problema, que você deve ter adivinhado, é que roundTo2DP não pode ser implementado para dar essas respostas óbvias, porque todos os três números passados ​​para ele são o mesmo número . Os números binários de ponto flutuante IEEE 754 (o tipo usado pelo JavaScript) não podem representar exatamente a maioria dos números não inteiros e, portanto, todos os três literais numéricos acima são arredondados para um número de ponto flutuante válido nas proximidades. Esse número, por acaso, é exatamente

0.01499999999999999944488848768742172978818416595458984375

que é mais próximo de 0,01 do que 0,02.

Você pode ver que todos os três números são iguais no console do navegador, no shell do nó ou em outro intérprete JavaScript. Basta compará-los:

> 0.014999999999999999 === 0.0150000000000000001
true

Então, quando escrevo m = 0.0150000000000000001, o valor exato dom qual acabo é mais próximo 0.01do que é 0.02. E, no entanto, se eu converter mpara uma String ...

> var m = 0.0150000000000000001;
> console.log(String(m));
0.015
> var m = 0.014999999999999999;
> console.log(String(m));
0.015

... recebo 0,015, que deve arredondar para 0,02, e que não é notavelmente o número de 56 casas decimais que eu disse anteriormente que todos esses números eram exatamente iguais a. Então, que magia negra é essa?

A resposta pode ser encontrada na especificação ECMAScript, na seção 7.1.12.1: ToString aplicada ao tipo Number . Aqui são estabelecidas as regras para converter algum número m em uma string. A parte da chave é o ponto 5, no qual um número inteiro s é gerado cujos dígitos serão usados ​​na representação String de m :

Seja n , k e s números inteiros tais que k ≥ 1, 10 k -1s <10 k , o valor numérico para s × 10 n - k é m , e k é o menor possível. Observe que k é o número de dígitos na representação decimal de s , que s não é divisível por 10 e que o dígito menos significativo de s não é necessariamente determinado exclusivamente por esses critérios.

A parte principal aqui é a exigência de que " k seja o menor possível". O que esse requisito equivale é um requisito que, dado um Número m, o valor de String(m)deve ter o menor número possível de dígitos enquanto ainda satisfaz o requisito que Number(String(m)) === m. Como já sabemos disso 0.015 === 0.0150000000000000001, agora está claro por que String(0.0150000000000000001) === '0.015'deve ser verdade.

Obviamente, nenhuma dessas discussões respondeu diretamente o que roundTo2DP(m) deveria retornar. Se mo valor exato for 0,01499999999999999944488848768742172978818416595458984375, mas sua representação de String for '0,015', qual é a resposta correta - matematicamente, praticamente, filosoficamente ou o que for - quando o arredondamos para duas casas decimais?

Não existe uma resposta correta para isso. Depende do seu caso de uso. Você provavelmente deseja respeitar a representação String e arredondar para cima quando:

  • O valor que está sendo representado é inerentemente discreto, por exemplo, uma quantidade de moeda em uma moeda de 3 casas decimais, como dinares. Nesse caso, o valor verdadeiro de um número como 0,015 é 0,015 e a representação 0,0149999999 ... que chega no ponto flutuante binário é um erro de arredondamento. (Obviamente, muitos argumentarão, razoavelmente, que você deve usar uma biblioteca decimal para manipular esses valores e nunca representá-los como números binários de ponto flutuante em primeiro lugar.)
  • O valor foi digitado por um usuário. Nesse caso, novamente, o número decimal exato inserido é mais 'verdadeiro' do que a representação binária de ponto flutuante mais próxima.

Por outro lado, você provavelmente quer respeitar o valor do ponto flutuante binário e arredondar para baixo quando o valor for de uma escala inerentemente contínua - por exemplo, se for uma leitura de um sensor.

Essas duas abordagens requerem código diferente. Para respeitar a representação String do número, podemos (com um pouco de código razoavelmente sutil) implementar nosso próprio arredondamento que atua diretamente na representação String, dígito por dígito, usando o mesmo algoritmo que você usaria na escola quando foram ensinados a arredondar números. Abaixo está um exemplo que respeita o requisito do OP de representar o número com 2 casas decimais "somente quando necessário" eliminando os zeros à direita após o ponto decimal; você pode, é claro, precisar ajustá-lo às suas necessidades precisas.

/**
 * Converts num to a decimal string (if it isn't one already) and then rounds it
 * to at most dp decimal places.
 *
 * For explanation of why you'd want to perform rounding operations on a String
 * rather than a Number, see http://stackoverflow.com/a/38676273/1709587
 *
 * @param {(number|string)} num
 * @param {number} dp
 * @return {string}
 */
function roundStringNumberWithoutTrailingZeroes (num, dp) {
    if (arguments.length != 2) throw new Error("2 arguments required");

    num = String(num);
    if (num.indexOf('e+') != -1) {
        // Can't round numbers this large because their string representation
        // contains an exponent, like 9.99e+37
        throw new Error("num too large");
    }
    if (num.indexOf('.') == -1) {
        // Nothing to do
        return num;
    }

    var parts = num.split('.'),
        beforePoint = parts[0],
        afterPoint = parts[1],
        shouldRoundUp = afterPoint[dp] >= 5,
        finalNumber;

    afterPoint = afterPoint.slice(0, dp);
    if (!shouldRoundUp) {
        finalNumber = beforePoint + '.' + afterPoint;
    } else if (/^9+$/.test(afterPoint)) {
        // If we need to round up a number like 1.9999, increment the integer
        // before the decimal point and discard the fractional part.
        finalNumber = Number(beforePoint)+1;
    } else {
        // Starting from the last digit, increment digits until we find one
        // that is not 9, then stop
        var i = dp-1;
        while (true) {
            if (afterPoint[i] == '9') {
                afterPoint = afterPoint.substr(0, i) +
                             '0' +
                             afterPoint.substr(i+1);
                i--;
            } else {
                afterPoint = afterPoint.substr(0, i) +
                             (Number(afterPoint[i]) + 1) +
                             afterPoint.substr(i+1);
                break;
            }
        }

        finalNumber = beforePoint + '.' + afterPoint;
    }

    // Remove trailing zeroes from fractional part before returning
    return finalNumber.replace(/0+$/, '')
}

Exemplo de uso:

> roundStringNumberWithoutTrailingZeroes(1.6, 2)
'1.6'
> roundStringNumberWithoutTrailingZeroes(10000, 2)
'10000'
> roundStringNumberWithoutTrailingZeroes(0.015, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.015000', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(1, 1)
'1'
> roundStringNumberWithoutTrailingZeroes('0.015', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(0.01499999999999999944488848768742172978818416595458984375, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.01499999999999999944488848768742172978818416595458984375', 2)
'0.01'

A função acima é provavelmente o que você deseja usar para evitar que os usuários assistam aos números digitados serem arredondados incorretamente.

(Como alternativa, você também pode tentar o biblioteca round10 , que fornece uma função de comportamento semelhante com uma implementação totalmente diferente.)

Mas e se você tiver o segundo tipo de Número - um valor extraído de uma escala contínua, onde não há razão para pensar que representações decimais aproximadas com menos casas decimais são mais precisas do que aquelas com mais? Nesse caso, nós não queremos respeitar a representação String, porque essa representação (conforme explicado na especificação) já é do tipo arredondada; não queremos cometer o erro de dizer "0,014999999 ... 375 arredonda para 0,015, que arredonda para 0,02, então 0,014999999 ... 375 arredonda para 0,02".

Aqui podemos simplesmente usar o toFixedmétodo embutido . Observe que, chamando Number()a String retornada por toFixed, obtemos um Número cuja representação de String não possui zeros à direita (graças à maneira como o JavaScript calcula a representação de String de um Número, discutida anteriormente nesta resposta).

/**
 * Takes a float and rounds it to at most dp decimal places. For example
 *
 *     roundFloatNumberWithoutTrailingZeroes(1.2345, 3)
 *
 * returns 1.234
 *
 * Note that since this treats the value passed to it as a floating point
 * number, it will have counterintuitive results in some cases. For instance,
 * 
 *     roundFloatNumberWithoutTrailingZeroes(0.015, 2)
 *
 * gives 0.01 where 0.02 might be expected. For an explanation of why, see
 * http://stackoverflow.com/a/38676273/1709587. You may want to consider using the
 * roundStringNumberWithoutTrailingZeroes function there instead.
 *
 * @param {number} num
 * @param {number} dp
 * @return {number}
 */
function roundFloatNumberWithoutTrailingZeroes (num, dp) {
    var numToFixedDp = Number(num).toFixed(dp);
    return Number(numToFixedDp);
}
Mark Amery
fonte
Isso não funciona em alguns casos extremos : tente ( jsfiddle ) com roundStringNumberWithoutTrailingZeroes(362.42499999999995, 2). Resultado esperado (como em PHP echo round(362.42499999999995, 2)): 362.43. Resultado real:362.42
Dr. Gianluigi Zane Zanettini
1
@ Dr.GianluigiZaneZanettini Huh. Esquisito. Não sei por que o PHP roundoferece 362,43. Isso parece intuitivamente errado, já que 362.42499999999995 é menor que 362.425 (em matemática e em código - 362.42499999999995 < 362.425é verdade tanto em JS quanto em PHP). A resposta do PHP também não minimiza a distância entre os números de ponto flutuante original e arredondado, desde então 362.43 - 362.42499999999995 > 362.42499999999995 - 362.42. De acordo com php.net/manual/en/function.round.php , o PHP roundsegue o padrão C99; Vou ter que me aventurar na terra de C para entender o que está acontecendo.
Mark Amery
1
Depois de analisar a implementação do arredondamento na fonte do PHP , não faço a menor idéia do que está fazendo. É uma implementação terrivelmente complicada, cheia de macros, ramificações e conversões de string e ramificação em valores de precisão mágica codificados. Não tenho certeza do que dizer além de "A resposta do PHP está descaradamente errada, e devemos registrar um relatório de erro". Como você encontrou o número 362.42499999999995, a propósito?
Mark Amery
1
@ Dr.GianluigiZaneZanettini Eu criei um relatório de bug: bugs.php.net/bug.php?id=75644 #
Mark Amery
2
@ Dr.GianluigiZaneZanettini "Eu faço algum sentido?" - não; não é assim que o arredondamento funciona. 1.000005 termina em cinco, mas se arredondada para o número inteiro mais próximo, a resposta deve ser 1, e não 2. Da mesma forma, 1499995 termina em cinco, mas se arredondada para o milhão mais próximo, o resultado deve ser 1000000, e não 2000000. No caso de 362.42499999999995 sendo arredondado para 2 DP, o que deve determinar a direção do arredondamento é a terceira casa decimal, que é 4. #
Mark Amery
76

Considere .toFixed()e .toPrecision():

http://www.javascriptkit.com/javatutors/formatnumber.shtml

AceCorban
fonte
1
O toFixed adiciona os pontos decimais a todos os valores, não importa o quê.
Stinkycheeseman
13
Ambos são inúteis aqui
Esailija
Infelizmente, ambas as funções adicionam casas decimais extras que @stinkycheeseman parece não querer.
jackwanders
2
eles retornam strings e não números, então eles estão a formatação e não calcular ..
saimiris_devel
63

Um método de arredondamento preciso. Fonte: Mozilla

(function(){

    /**
     * Decimal adjustment of a number.
     *
     * @param   {String}    type    The type of adjustment.
     * @param   {Number}    value   The number.
     * @param   {Integer}   exp     The exponent (the 10 logarithm of the adjustment base).
     * @returns {Number}            The adjusted value.
     */
    function decimalAdjust(type, value, exp) {
        // If the exp is undefined or zero...
        if (typeof exp === 'undefined' || +exp === 0) {
            return Math[type](value);
        }
        value = +value;
        exp = +exp;
        // If the value is not a number or the exp is not an integer...
        if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
            return NaN;
        }
        // Shift
        value = value.toString().split('e');
        value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
        // Shift back
        value = value.toString().split('e');
        return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
    }

    // Decimal round
    if (!Math.round10) {
        Math.round10 = function(value, exp) {
            return decimalAdjust('round', value, exp);
        };
    }
    // Decimal floor
    if (!Math.floor10) {
        Math.floor10 = function(value, exp) {
            return decimalAdjust('floor', value, exp);
        };
    }
    // Decimal ceil
    if (!Math.ceil10) {
        Math.ceil10 = function(value, exp) {
            return decimalAdjust('ceil', value, exp);
        };
    }
})();

Exemplos:

// Round
Math.round10(55.55, -1); // 55.6
Math.round10(55.549, -1); // 55.5
Math.round10(55, 1); // 60
Math.round10(54.9, 1); // 50
Math.round10(-55.55, -1); // -55.5
Math.round10(-55.551, -1); // -55.6
Math.round10(-55, 1); // -50
Math.round10(-55.1, 1); // -60
Math.round10(1.005, -2); // 1.01 -- compare this with Math.round(1.005*100)/100 above
// Floor
Math.floor10(55.59, -1); // 55.5
Math.floor10(59, 1); // 50
Math.floor10(-55.51, -1); // -55.6
Math.floor10(-51, 1); // -60
// Ceil
Math.ceil10(55.51, -1); // 55.6
Math.ceil10(51, 1); // 60
Math.ceil10(-55.59, -1); // -55.5
Math.ceil10(-59, 1); // -50
do utilizador
fonte
Alguém colocou isso no GitHub e no npm também: github.com/jhohlfeld/round10
Jo Liss
Nah, Math.round10(3544.5249, -2)retorna 3544,52 em vez de 3544,53
Matija
2
@Matija Wolfram alpha também diz 3544,52. Você deseja minimizar o erro entre o número atual e a aproximação arredondada. A aproximação mais próxima de 3544.5249duas casas decimais é 3544.52(erro = 0,0049). Se fosse 3544.53, o erro seria 0,0051. Você está fazendo um arredondamento sucessivo, ou seja, Math.round10 (Math.round10 (3544.5249, -3), -2), que gera um erro de arredondamento maior e, portanto, não é desejável.
usuário
3
@Matija, do ponto de vista matemático, 3544.5249 arredondado é 3544.52, não 3544.53, portanto esse código está correto. Se você deseja arredondá-lo para 3544,53 neste caso e em casos como este (mesmo que difícil seja incorreto), faça algo assim:number += 0.00011
Bozidar Sikanjic 16/16
@Matija: Eu acho que a função funciona como deveria. Talvez você queira iterar o arredondamento como:Math.round10( Math.round10(3544.5249, -3) , -2)
jumxozizi
60

Nenhuma das respostas encontradas aqui está correta . @stinkycheeseman pediu para arredondar , todos vocês arredondaram o número.

Para arredondar, use o seguinte:

Math.ceil(num * 100)/100;
machineaddict
fonte
15
O exemplo de entrada e saída mostra que, embora a pergunta dissesse 'arredondar para cima ...', na verdade pretendia ser 'arredondar para ...'.
21413 JayDM
2
@stinkycheeseman estava apontando um erro em um caso específico, ele não queria se sempre redonda como ceil faz, ele só queria 0,005 a rodada até 0,01
mjaggard
9
Foi encontrado um bug estranho durante o teste Math.ceil(1.1 * 100)/100;- ele retorna 1.11, porque o 1.1 * 100 está de 110.00000000000001acordo com os novos navegadores modernos Firefox, Chrome, Safari e Opera ... IE, à moda antiga, ainda pensa 1.1*100=1100.
precisa saber é o seguinte
1
@skobaljic tryMath.ceil(num.toFixed(4) * 100) / 100
treeface
1
O @treeface Math.ceil((1.1).toFixed(4) * 100) / 100também retornará 1.11no Firefox, o problema / bug dos navegadores modernos é a multiplicação e as pessoas devem saber disso (trabalhei em um jogo de loteria dessa vez, por exemplo).
skobaljic
47

Aqui está uma maneira simples de fazer isso:

Math.round(value * 100) / 100

Você pode ir em frente e criar uma função separada para fazer isso por você:

function roundToTwo(value) {
    return(Math.round(value * 100) / 100);
}

Então você simplesmente passaria o valor.

Você pode aprimorá-lo para arredondar para qualquer número arbitrário de casas decimais adicionando um segundo parâmetro.

function myRound(value, places) {
    var multiplier = Math.pow(10, places);

    return (Math.round(value * multiplier) / multiplier);
}
JayDM
fonte
2
Esta solução está errada, consulte stackoverflow.com/questions/38322372/… se você inserir 156893.145 e arredondá-la com a função acima, obtém 156893.14 em vez de 156893.15 !!!
21416 saharais_devel
2
@saimiris_devel, você conseguiu encontrar uma instância em que a representação numérica de números no JavaScript falha. Você está correto ao arredondar incorretamente. Mas - isso ocorre porque o número da amostra, quando multiplicado por 100, já está quebrado (15689314.499999998). Realmente, uma resposta com todos os recursos exigiria uma biblioteca desenvolvida especificamente para levar em conta as inconsistências no manuseio de números reais pelo JavaScript. Caso contrário, você provavelmente poderá invalidar qualquer uma das respostas que foram dadas a esta pergunta.
JayDM
36
+(10).toFixed(2); // = 10
+(10.12345).toFixed(2); // = 10.12

(10).toFixed(2); // = 10.00
(10.12345).toFixed(2); // = 10.12
user3711536
fonte
1
Isso nem sempre dará os mesmos resultados que você obteria se pegasse a representação String do seu Número e a arredondasse. Por exemplo +(0.015).toFixed(2) == 0.01,.
Mark Amery
35

Para mim, Math.round () não estava dando a resposta correta. Achei que oFixed (2) funciona melhor. Abaixo estão exemplos de ambos:

console.log(Math.round(43000 / 80000) * 100); // wrong answer

console.log(((43000 / 80000) * 100).toFixed(2)); // correct answer

Vikasdeep Singh
fonte
Importante notar que toFixed não executa um arredondamento e que Math.round apenas arredonda para o número inteiro mais próximo. Para preservar as casas decimais, precisamos multiplicar o número original pelo número de potências de dez cujos zeros representam o número desejado de casas decimais e depois dividir o resultado pelo mesmo número. No seu caso: Math.round (43000/80000 * 100 * 100) / 100. Finalmente, oFixed (2) pode ser aplicado para garantir que sempre haja duas casas decimais no resultado (com zeros à direita quando necessário) - perfeito para-direita do alinhamento de uma série de números apresentados verticalmente :)
Turbo
7
Também é importante observar que .toFIxed () gera uma string, não um número.
26918 carpiediem
2
Isso ainda não resolve o arredondamento 1.005. (1.005).toFixed(2)ainda resulta em 1.00.
DPac
34

Use esta função Number(x).toFixed(2);

Harish.bazee
fonte
8
Envolva tudo em Numbernovamente, se você não quer que ele voltou como uma string:Number(Number(x).toFixed(2));
5
A Numberligação não é necessária, x.toFixed(2)funciona.
bgusach
3
@bgusach Número de chamada necessário, pois a instrução x.toFixed (2) retorna uma string e não um número. Para converter novamente em número, precisamos encerrar com Number
Mohan Ram
2
Ao usar esse método , (1).toFixed(2)retorna 1.00, mas o questionador é necessário 1neste caso.
Eugene Mala
1
Isso não funciona, 1.005.toFixed(2)rende "1"quando deveria ser "1.01".
Adam Jagosz 28/08/19
33

2017
Basta usar código nativo.toFixed()

number = 1.2345;
number.toFixed(2) // "1.23"

Se você precisar ser rigoroso e adicionar dígitos, se necessário, ele poderá usar replace

number = 1; // "1"
number.toFixed(5).replace(/\.?0*$/g,'');
pery mimon
fonte
3
O método toFixed retorna uma string. Se você quiser um resultado numérico, precisará enviar o resultado de toFixed para parseFloat.
Zambonilli 7/11
@ Zambonilli Ou apenas multiplique por 1, se necessário. mas porque o número fixo maioria dos casos são para exibição e não para cadeia de cálculo é o formato certo
pery mimon
2
-1; não foi toFixedsugerido apenas por várias respostas anos antes da sua, mas também não satisfaz a condição "somente se necessário" da pergunta; (1).toFixed(2)"1.00"onde o autor da pergunta desejou "1".
Mark Amery
OK, entendi. Eu adiciono alguma solução também para esse caso
pery mimon
Se você estiver usando o lodash, é ainda mais fácil: _.round (number, decimalPlace) Excluído meu último comentário, porque ele tem um problema. Lodash _.round funciona, no entanto. 1.005 com casa decimal de 2 converte para 1,01.
Devin Campos
32

Experimente esta solução leve :

function round(x, digits){
  return parseFloat(x.toFixed(digits))
}

 round(1.222,  2) ;
 // 1.22
 round(1.222, 10) ;
 // 1.222
petermeissner
fonte
Alguém sabe se existe alguma diferença entre isso e return Number(x.toFixed(digits))?
1
@JoeRocc ... não deve fazer diferença, tanto quanto eu posso ver, uma vez que .toFixed()permite apenas números de qualquer maneira.
Petermeissner 12/09/2015
4
Esta resposta tem o mesmo problema que mencionado várias vezes nesta página. Experimente round(1.005, 2)e veja o resultado de em 1vez de 1.01.
MilConDoin
parece mais um problema do arredondamento algo? - há mais do que se poderia imaginar: en.wikipedia.org/wiki/Rounding ... round(0.995, 2) => 0.99; round(1.006, 2) => 1.01; round(1.005, 2) => 1
Petermeissner
31

Existem algumas maneiras de fazer isso. Para pessoas como eu, a variante do Lodash

function round(number, precision) {
    var pair = (number + 'e').split('e')
    var value = Math.round(pair[0] + 'e' + (+pair[1] + precision))
    pair = (value + 'e').split('e')
    return +(pair[0] + 'e' + (+pair[1] - precision))
}

Uso:

round(0.015, 2) // 0.02
round(1.005, 2) // 1.01

Se o seu projeto usa jQuery ou lodash, você também pode encontrar round método nas bibliotecas.

Atualização 1

Eu removi a variante n.toFixed(2), porque não está correta. Obrigado @ avalanche1

stanleyxu2005
fonte
A segunda opção retornará uma string com exatamente duas casas decimais. A pergunta pede pontos decimais somente se necessário. A primeira opção é melhor neste caso.
Marcos Lima
O @MarcosLima Number.toFixed()retornará uma string, mas com um símbolo de adição antes, o interpretador JS converterá a string em um número. Este é um açúcar de sintaxe.
precisa saber é o seguinte
No Firefox, alert((+1234).toFixed(2))mostra "1234.00".
Marcos Lima
No Firefox, alert(+1234.toFixed(2))joga SyntaxError: identifier starts immediately after numeric literal. Eu fico com a 1ª opção.
Marcos Lima
Isso não funciona em alguns casos extremos : tente ( jsfiddle ) com 362.42499999999995. Resultado esperado (como em PHP echo round(362.42499999999995, 2)): 362.43. Resultado real:362.42
Dr. Gianluigi Zane Zanettini
26

Se você estiver usando a biblioteca lodash, poderá usar o método round da lodash como a seguir.

_.round(number, precision)

Por exemplo:

_.round(1.7777777, 2) = 1.78
Madura Pradeep
fonte
@ Peter O conjunto de funcionalidades que o Lodash fornece é realmente bom comparado ao Javascript padrão. No entanto, ouvi dizer que o Lodash tem algum problema de desempenho em comparação com o JS padrão. codeburst.io/…
Madura Pradeep
1
Aceito seu argumento de que há desvantagens de desempenho ao usar o lodash. Eu acho que esses problemas são comuns a muitas abstrações. Mas observe quantas respostas existem nesse segmento e como as soluções intuitivas falham em casos extremos. Vimos esse padrão com o jQuery e o problema raiz foi resolvido quando os navegadores adotaram um padrão comum que solucionava a maioria dos nossos casos de uso. Os gargalos de desempenho foram então movidos para os mecanismos do navegador. Eu acho que o mesmo deve acontecer com o lodash. :)
Pedro
26

Desde o ES6, existe uma maneira 'adequada' (sem substituir as estáticas e criar soluções alternativas) para fazer isso usando toPrecision

var x = 1.49999999999;
console.log(x.toPrecision(4));
console.log(x.toPrecision(3));
console.log(x.toPrecision(2));

var y = Math.PI;
console.log(y.toPrecision(6));
console.log(y.toPrecision(5));
console.log(y.toPrecision(4));

var z = 222.987654
console.log(z.toPrecision(6));
console.log(z.toPrecision(5));
console.log(z.toPrecision(4));

então você pode simplesmente parseFloate os zeros 'desaparecem'.

console.log(parseFloat((1.4999).toPrecision(3)));
console.log(parseFloat((1.005).toPrecision(3)));
console.log(parseFloat((1.0051).toPrecision(3)));

No entanto, ele não resolve o 'problema de arredondamento 1.005' - pois é intrínseco ao modo como as frações flutuantes estão sendo processadas .

console.log(1.005 - 0.005);

Se você estiver aberto para bibliotecas, poderá usar o bignumber.js

console.log(1.005 - 0.005);
console.log(new BigNumber(1.005).minus(0.005));

console.log(new BigNumber(1.005).round(4));
console.log(new BigNumber(1.005).round(3));
console.log(new BigNumber(1.005).round(2));
console.log(new BigNumber(1.005).round(1));
<script src="https://cdnjs.cloudflare.com/ajax/libs/bignumber.js/2.3.0/bignumber.min.js"></script>

Matas Vaitkevicius
fonte
3
(1.005).toPrecision(3)ainda retorna em 1.00vez de 1.01realmente.
Giacomo
toPrecisionretorna uma string que altera o tipo de saída desejado.
Adamduren
@ Giacomo Não é uma falha de .toPrecisionmétodo, é uma especificidade de números de ponto flutuante (que são números em JS) - tente 1.005 - 0.005, ele retornará 0.9999999999999999.
shau-kote
1
(1).toPrecision(3)retorna '1,00', mas o questionador queria ter 1nesse caso.
Eugene Mala
1
Como o @Giacomo disse, esta resposta parece confundir "dígitos significativos" com "arredondamento para um número de casas decimais". toPrecisionfaz o formato, não o último, e não é uma resposta para a pergunta do OP, embora possa parecer relevante a princípio, ele erra muito. Consulte en.wikipedia.org/wiki/Significant_figures . Por exemplo Number(123.4).toPrecision(2)retornos "1.2e+2"e Number(12.345).toPrecision(2)retornos "12". Eu também concordo com o argumento de @ adamduren de que ele retorna uma string que não é desejável (não é um grande problema, mas não é desejável).
Neek
23

MarkG e Lavamantis ofereceram uma solução muito melhor do que a que foi aceita. É uma pena que eles não recebam mais votos!

Aqui está a função que eu uso para resolver os problemas de decimais de ponto flutuante também baseados no MDN . É ainda mais genérico (mas menos conciso) do que a solução de Lavamantis:

function round(value, exp) {
  if (typeof exp === 'undefined' || +exp === 0)
    return Math.round(value);

  value = +value;
  exp  = +exp;

  if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0))
    return NaN;

  // Shift
  value = value.toString().split('e');
  value = Math.round(+(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp)));

  // Shift back
  value = value.toString().split('e');
  return +(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp));
}

Use-o com:

round(10.8034, 2);      // Returns 10.8
round(1.275, 2);        // Returns 1.28
round(1.27499, 2);      // Returns 1.27
round(1.2345678e+2, 2); // Returns 123.46

Comparado à solução da Lavamantis, podemos fazer ...

round(1234.5678, -2); // Returns 1200
round("123.45");      // Returns 123
astorije
fonte
2
Sua solução não cobre alguns casos, em oposição à solução da MDN. Embora possa ser mais curto, não é preciso ...
astorije
1
round (-1835.665,2) => -1835.66 #
Jorge Sampayo
21

Isso pode ajudá-lo a:

var result = Math.round(input*100)/100;

para mais informações, você pode dar uma olhada neste link

Math.round (num) vs num.toFixed (0) e inconsistências do navegador

totten
fonte
1
Por que no mundo a resposta aceita tem mais votos do que este, uma vez que é praticamente a mesma coisa, mas foi postada 1 minuto após a resposta?
Cite Dave
18

A abordagem mais fácil seria usar o toFixed e depois remover zeros à direita usando a função Number:

const number = 15.5;
Number(number.toFixed(2)); // 15.5
const number = 1.7777777;
Number(number.toFixed(2)); // 1.78
Marcin Wanago
fonte
isso não funciona para todos os casos. faça testes extensivos antes de postar respostas.
baburao 19/04
@baburao Por favor, poste um caso em que a solução acima não funcione
Marcin Wanago
número const = 15; Number (number.toFixed (2)); // 15,00 em vez de 15
Kevin Jhangiani
1
@KevinJhangiani número const = 15; Number (number.toFixed (2)); // 15 - Eu testei tanto no Chrome quanto no Firefox mais recentes
Marcin Wanago
@KevinJhangiani como você consegue 15.00? Os números em JS não armazenam as casas decimais e qualquer exibição trunca automaticamente as casas decimais em excesso (quaisquer zeros no final).
VLAZ
16
var roundUpto = function(number, upto){
    return Number(number.toFixed(upto));
}
roundUpto(0.1464676, 2);

toFixed(2) aqui 2 é o número de dígitos até o qual queremos arredondar esse número.

Ritesh Dhuri
fonte
este .toFixed () é mais simples de implementar. basta passar por isso uma vez.
Ritesh Dhuri
14

Caminho mais fácil:

+num.toFixed(2)

Ele o converte em uma sequência e depois volta para um número inteiro / float.

bigpotato
fonte
Obrigado por esta resposta mais simples. No entanto, o que é '+' em + num? Não funcionou para mim onde o valor decimal veio na string. Eu fiz: (num * 1) .toFixed (2).
Ethan
@ Momo basta alterar o argumento toFixed()para 3. Assim seria +num.toFixed(3). Isso funciona da forma como é suposto, 1.005 é arredondado para 1,00, o que é igual a 1
bigpotato
1
@ Edmund É suposto retornar 1.01, não 1.00
mmm
13

Aqui está um método de protótipo:

Number.prototype.round = function(places){
    places = Math.pow(10, places); 
    return Math.round(this * places)/places;
}

var yournum = 10.55555;
yournum = yournum.round(2);
arielf
fonte
13

Use algo parecido com isto "parseFloat (parseFloat (value) .toFixed (2))"

parseFloat(parseFloat("1.7777777").toFixed(2))-->1.78 
parseFloat(parseFloat("10").toFixed(2))-->10 
parseFloat(parseFloat("9.1").toFixed(2))-->9.1
Arulraj
fonte
1
não se a imprecisão for intrínseca à representação flutuante. você apenas o removeria e reintroduziria o mesmo erro, convertendo novamente para flutuar novamente!
Ben McIntyre
12

Uma maneira de obter esse arredondamento apenas se necessário é usar Number.prototype.toLocaleString () :

myNumber.toLocaleString('en', {maximumFractionDigits:2, useGrouping:false})

Isso fornecerá exatamente a saída que você espera, mas como strings. Você ainda pode convertê-los novamente em números, se esse não for o tipo de dados que você espera.

Javarome
fonte
Esta é a solução mais limpa que existe, de longe e supera todos os problemas complicados de ponto flutuante, mas o suporte a MDN ainda está incompleto - o Safari ainda não suporta a passagem de argumentos toLocaleString.
Mark Amery
@MarkAmery Por enquanto, apenas o Navegador Android tem alguns problemas: caniuse.com/#search=toLocaleString
ptyskju
12

Depois de executar várias iterações de todas as formas possíveis para obter uma precisão exata de arredondamento decimal, é claro que a solução mais precisa e eficiente é usar Number.EPSILON. Isso fornece uma verdadeira solução matemática para o problema da precisão matemática de ponto flutuante. Ele pode ser facilmente preenchido como mostrado aqui: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON para oferecer suporte a todos os últimos usuários restantes do IE (talvez seja necessário deve parar de fazer isso).

Adaptado da solução fornecida aqui: https://stackoverflow.com/a/48850944/6910392

Uma solução simples que fornece arredondamento decimal preciso, piso e teto, com uma variável de precisão opcional sem adicionar uma biblioteca inteira.

ATUALIZAÇÃO: Como Sergey observou nos comentários, há uma limitação a este (ou a qualquer outro) método que vale a pena ressaltar. No caso de números como 0,014999999999999999, você ainda terá imprecisões que resultam de atingir o limite absoluto das limitações de precisão para armazenamento de valor de ponto flutuante. Não há matemática ou outra solução que possa ser aplicada para explicar isso, pois o valor em si é imediatamente avaliado como 0,015. Você pode confirmar isso simplesmente invocando esse valor sozinho no console. Devido a essa limitação, nem seria possível usar a manipulação de strings para reduzir esse valor, pois sua representação de strings é simplesmente "0,015". Qualquer solução para explicar isso precisaria ser aplicada logicamente na origem dos dados antes de aceitar o valor em um script,

var DecimalPrecision = (function(){
        if (Number.EPSILON === undefined) {
            Number.EPSILON = Math.pow(2, -52);
        }
        this.round = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.round((n + r) * o) / o;
        }
        this.ceil = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.ceil((n + r) * o) / o;
        }
        this.floor = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.floor((n + r) * o) / o;
        }
        return this;
    })();
    console.log(DecimalPrecision.round(1.005));
    console.log(DecimalPrecision.ceil(1.005));
    console.log(DecimalPrecision.floor(1.005));
    console.log(DecimalPrecision.round(1.0049999));
    console.log(DecimalPrecision.ceil(1.0049999));
    console.log(DecimalPrecision.floor(1.0049999));
    console.log(DecimalPrecision.round(2.175495134384,7));
    console.log(DecimalPrecision.round(2.1753543549,8));
    console.log(DecimalPrecision.round(2.1755465135353,4));

KFish
fonte
1
(DecimalPrecision.round (0.014999999999999999, 2)) // retorna 0,02
Sergey
Boa pegada! O problema é com o armazenamento de ponto flutuante em JS; sempre haverá alguns casos extremos. A boa notícia é que a matemática que você aplica ao Number.EPSILON primeiro pode ser ajustada com mais precisão para empurrar ainda mais esses casos extremos. Se você deseja garantir nenhuma possibilidade para casos extremos, sua única solução real será a manipulação de strings e, depois, a matemática. No momento em que você executa qualquer cálculo matemático no valor (mesmo tentando mover o decimal), já produziu o erro.
KFish
Na verdade, em uma inspeção mais aprofundada, isso não se deve a nenhuma das matemáticas envolvidas, mas o problema se manifesta imediatamente após a invocação do valor especificado. Você pode confirmar isso digitando esse número no console e ver se ele é imediatamente avaliado em 0,015. Portanto, isso representaria a borda absoluta da precisão para qualquer número de ponto flutuante em JS. Nesse caso, você não pode nem converter para string e manipular, pois o valor da string seria "0,015"
KFish
11

Essa é a solução mais simples e elegante (e eu sou a melhor do mundo;):

function roundToX(num, X) {    
    return +(Math.round(num + "e+"+X)  + "e-"+X);
}
//roundToX(66.66666666,2) => 66.67
//roundToX(10,2) => 10
//roundToX(10.904,2) => 10.9
Soldeplata Saketos
fonte
4
Essa é uma boa maneira de reescrever a resposta aceita para aceitar um argumento usando a Enotação.
AxelH
1
Isso não funciona em alguns casos extremos : try ( jsfiddle ) roundToX(362.42499999999995, 2). Resultado esperado (como em PHP echo round(362.42499999999995, 2)): 362.43. Resultado real:362.42
Dr. Gianluigi Zane Zanettini
6
IMHO, seu resultado PHP está errado. Não importa o que vem depois da terceira casa decimal, se a terceira casa decimal for menor que 5, a segunda casa decimal deve permanecer a mesma. Essa é a definição matemática.
Soldeplata Saketos
1
Para ser ainda mais conciso, "e +" pode ser apenas "e".
Lonnie Best