Como arredondar um número em JavaScript? .toFixed () retorna uma string?

176

Estou faltando alguma coisa aqui?

var someNumber = 123.456;
someNumber = someNumber.toFixed(2);
alert(typeof(someNumber));
//alerts string

Por que.toFixed()retornar uma string?

Quero arredondar o número para 2 dígitos decimais.

Derek Adair
fonte
7
Porque ele foi projetado para retornar uma string?
Kennytm
2
Para mim, isso parece estranho. .toFixed () opera apenas em números ... certo?
Derek Adair
10
Eu entendo Math.round () funciona como esperado. Eu estava apenas perguntando por que uma função que opera com números retorna uma string ...
Derek Adair
3
As pessoas que vivem em 2017 devem usar bibliotecas como lodash.com/docs/4.17.4#ceil
Yves M.
1
O mesmo acontece com _. contagem? ainda não atualizado para o irmão.
Folha Jenna

Respostas:

124

Ele retorna uma string porque 0,1 e suas potências (que são usadas para exibir frações decimais) não são representáveis ​​(pelo menos não com precisão total) em sistemas binários de ponto flutuante.

Por exemplo, 0.1 é realmente 0.1000000000000000055511151231257827021181583404541015625 e 0.01 é realmente 0.01000000000000000020816681711721685132943093776702880859375. (Obrigado BigDecimalpor provar meu ponto. :-P)

Portanto (sem um ponto flutuante decimal ou um tipo de número racional), a saída como uma sequência é a única maneira de ajustá-lo exatamente à precisão necessária para a exibição.

Chris Jester-Young
fonte
28
pelo menos javascript poderia me poupar algum dedo-obra e convertê-lo de volta para um número ... sheesh ...
Derek Adair
10
@Derek: Sim, mas depois que você o converte novamente em um número, você se depara com os mesmos problemas de imprecisão novamente. :-P JS não possui ponto flutuante decimal ou números racionais.
Chris Jester-Young
1
@DerekAdair Escrevi recentemente um post que explica isso ainda mais, no qual você pode estar interessado. Aproveite! stackoverflow.com/a/27030789/13
Chris Jester-Young
7
Na verdade, isso me levou a fazer uma pesquisa bastante pesada sobre esse assunto! Obrigado por toda sua ajuda!
Derek Adair
2
Sua resposta é um pouco enganadora: toFixedé uma função de formatação, que tem o único objetivo de converter um número em uma sequência de caracteres, formatando-o usando o número especificado de casas decimais. O motivo pelo qual ele retorna uma sequência é porque ela deve retornar uma sequência e, se fosse nomeada toStringFixed, o OP não ficaria surpreso com os resultados. O único problema aqui é que o OP esperava que funcionasse como Math.round, sem consultar a referência JS.
Groo
177

Number.prototype.toFixedé uma função projetada para formatar um número antes de imprimi-lo. É da família de toString, toExponentiale toPrecision.

Para arredondar um número, você faria o seguinte:

someNumber = 42.008;
someNumber = Math.round( someNumber * 1e2 ) / 1e2;
someNumber === 42.01;

// if you need 3 digits, replace 1e2 with 1e3 etc.
// or just copypaste this function to your code:

function toFixedNumber(num, digits, base){
  var pow = Math.pow(base||10, digits);
  return Math.round(num*pow) / pow;
}

.

Ou, se você deseja uma função " nativa ", pode estender o protótipo:

Number.prototype.toFixedNumber = function(digits, base){
  var pow = Math.pow(base||10, digits);
  return Math.round(this*pow) / pow;
}
someNumber = 42.008;
someNumber = someNumber.toFixedNumber(2);
someNumber === 42.01;


//or even hexadecimal

someNumber = 0xAF309/256  //which is af3.09
someNumber = someNumber.toFixedNumber(1, 16);
someNumber.toString(16) === "af3.1";

No entanto , lembre-se de que poluir o protótipo é considerado ruim quando você está escrevendo um módulo, pois os módulos não devem ter efeitos colaterais. Portanto, para um módulo, use a primeira função .

m93a
fonte
12
Eu acho que essa é a melhor resposta. Evita a conversão de tipos. Molho impressionante!
Phil
1
Ótima resposta! No entanto ... eu pratico JavaScript há mais ou menos 20 anos, mas não consigo entender por que você está usando essa construção + (...) em torno do valor de retorno? Graças @sam para esfregar na :) Como eu nunca estou velho demais para aprender, por favor, descreva :-)
HammerNL
1
@HammerNL Apesar da convicção de Sam, ele não faz nada :) É apenas uma prática - faz os IDEs reconhecerem essa função como type Number. A coisa é que +(anyValue)sempre retorna um número - por exemplo. +("45")retorna 45, +(new Number(42))retorna 42. É como digitar com força a função. Se você fizer disso um hábito, você pode evitar um monte de erros :)
m93a
Por que isso não está inserido no javascript principal: s
webmaster
2
O resultado de someNumber = Math.round( 42.008 * 1e2 ) / 1e2;não é 42.01, é ~42.0099999999999980. Motivo: o número 42.01não existe e é arredondado para o número existente mais próximo. entre, números de prova por toPrecision(18)para imprimi-lo com todos os dígitos relevantes.
Wiimm 10/05/19
118

Eu resolvi esse problema alterando isso:

someNumber = someNumber.toFixed(2)

...para isso:

someNumber = +someNumber.toFixed(2);

No entanto, isso converterá o número em uma sequência e o analisará novamente, o que terá um impacto significativo no desempenho. Se você se preocupa com desempenho ou segurança de tipo, verifique as outras respostas também.

Eve juan
fonte
39
Não não não não não! Não faça isso! Conversão de número para string apenas para arredondar é uma prática muito ruim ! Em vez disso, faça someNumber = Math.round(someNumber * 1e2) / 1e2! Veja minha resposta para uma maneira mais generalizada.
M93a
@ m93a - por que é uma prática tão ruim?
jczaplew
3
@jczaplew Porque, se você fizer dessa maneira, o número binário de 32 bits será convertido em uma string, usando 16 bits para cada dígito decimal ! (A propósito, armazenar números em UTF-16 não é a coisa mais conveniente.) E a sequência é convertida novamente em um número de ponto flutuante de 32 bits. Dígito por dígito. (Se eu ignorar todos os testes que devem ser feitos antes para escolher o algoritmo de análise correto.) E tudo isso em vão, considerando que você pode fazê-lo usando 3 operações rápidas no flutuador.
M93a
2
@ m93a, por razões de desempenho? isso realmente tem um impacto perceptível no desempenho?
Sebastianb
2
@jczaplew, porque as strings são (1) praticamente lentas e (2) teoricamente incorretas.
Pacerier
28

Por que não usar parseFloat?

var someNumber = 123.456;
someNumber = parseFloat(someNumber.toFixed(2));
alert(typeof(someNumber));
//alerts number
sirlunchalot
fonte
15

Eu o resolvi convertendo-o de volta para o número usando a Number()função JavaScript

var x = 2.2873424;
x = Number(x.toFixed(2));
Nizar
fonte
12

Claro que retorna uma string. Se você quiser arredondar a variável numérica, use Math.round (). O ponto de toFixed é formatar o número com um número fixo de casas decimais para exibição no usuário .

Joel Coehoorn
fonte
4

Você pode simplesmente usar um '+' para converter o resultado em um número.

var x = 22.032423;
x = +x.toFixed(2); // x = 22.03
meisam
fonte
3

O que você esperaria que retornasse quando deveria formatar um número? Se você tem um número, não pode fazer nada com ele porque, por exemplo, 2 == 2.0 == 2.00etc., por isso deve ser uma string.

Tomas Vana
fonte
3

Para fornecer um exemplo de por que tem que ser uma string:

Se você formatar 1.toFixed (2), receberá '1.00'.

Não é o mesmo que 1, pois 1 não possui 2 casas decimais.


Sei que o JavaScript não é exatamente uma linguagem de desempenho , mas é provável que você obtenha melhor desempenho para um arredondamento se usar algo como: roundedValue = Math.round (valor * 100) * 0.01

Pyro
fonte
2

Porque seu uso principal é exibir números? Se você quiser arredondar números, use Math.round()com fatores apropriados.

Christoph
fonte
mas está exibindo NUMBERS, então não deveria retornar um "número"?
Derek Adair
3
@ Derek: Apenas da maneira que '42'é um número ... o que não é. Só porque uma string contém apenas dígitos não a torna um número. Isto não é PHP. :-P
Chris Jester-Young
ri muito. Não é uma string que contém um número ... É um número que é passado para um método. O método pega um número e retorna uma string.
Derek Adair
@DerekAdair certo, mas um navegador não pode exibir um número, ele exibe seqüências de caracteres e, portanto, a conversão.
Nick M
1

Aqui está uma versão um pouco mais funcional da resposta m93afornecida.

const toFixedNumber = (toFixTo = 2, base = 10) => num => {
  const pow = Math.pow(base, toFixTo)
  return +(Math.round(num * pow) / pow)
}

const oneNumber = 10.12323223

const result1 = toFixedNumber(2)(oneNumber) // 10.12
const result2 = toFixedNumber(3)(oneNumber) // 10.123

// or using pipeline-operator
const result3 = oneNumber |> toFixedNumber(2) // 10.12
Sartaj
fonte
é uma função útil, para o tipo indefinido não funciona, adicionei código para este caso; if (num! == indefinido) {return + (Math.round (num * pow) / pow)} else {retorno 0; }
Dino Liu