Comment arrondir un nombre en JavaScript? .toFixed () renvoie une chaîne?

176

Est-ce que j'ai râté quelque chose?

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

Pourquoi ne.toFixed()retourne une chaîne?

Je veux arrondir le nombre à 2 chiffres décimaux.

Derek Adair
la source
7
Parce qu'il est conçu pour renvoyer une chaîne?
kennytm
2
Pour moi, cela me semble étrange. .toFixed () ne fonctionne que sur les nombres ... non?
Derek Adair
10
Je comprends que Math.round () fonctionne comme prévu. Je voulais juste savoir pourquoi une fonction qui opère sur des nombres renvoie une chaîne ...
Derek Adair
3
Les personnes vivant en 2017 devraient utiliser des bibliothèques comme lodash.com/docs/4.17.4#ceil
Yves M.
1
Il en va de même pour _. compter? pas encore amélioré à son frère.
Jenna Leaf

Réponses:

124

Il renvoie une chaîne car 0,1 et ses puissances (qui sont utilisées pour afficher les fractions décimales) ne sont pas représentables (du moins pas avec une précision totale) dans les systèmes binaires à virgule flottante.

Par exemple, 0,1 est vraiment 0.1000000000000000055511151231257827021181583404541015625 et 0.01 est vraiment 0.01000000000000000020816681711721685132943093776702880859375. (Merci à d' BigDecimalavoir prouvé mon point. :-P)

Par conséquent (en l'absence de virgule flottante décimale ou de type nombre rationnel), la sortie sous forme de chaîne est le seul moyen de l'ajuster exactement à la précision requise pour l'affichage.

Chris Jester-Young
la source
28
au moins javascript pourrait me sauver du travail du doigt et le reconvertir en un nombre ... sheesh ...
Derek Adair
10
@Derek: Oui, mais une fois que vous le reconvertissez en nombre, vous rencontrez à nouveau les mêmes problèmes d'inexactitude. :-P JS n'a pas de virgule flottante décimale ou de nombres rationnels.
Chris Jester-Young
1
@DerekAdair J'ai récemment écrit un article qui explique encore plus cela, qui pourrait vous intéresser. Profitez-en! stackoverflow.com/a/27030789/13
Chris Jester-Young
7
En fait, cela m'a amené à faire des recherches assez lourdes sur ce sujet! Merci pour votre aide!
Derek Adair
2
Votre réponse est légèrement trompeuse: il toFixeds'agit d'une fonction de mise en forme, qui a pour seul but de convertir un nombre en chaîne, en le formatant en utilisant le nombre de décimales spécifié. La raison pour laquelle il renvoie une chaîne est parce qu'il est censé renvoyer une chaîne, et s'il était nommé à la toStringFixedplace, OP ne serait pas surpris des résultats. Le seul problème ici est que OP s'attendait à ce que cela fonctionne comme Math.round, sans consulter la référence JS.
Groo
177

Number.prototype.toFixedest une fonction conçue pour formater un nombre avant de l'imprimer. C'est de la famille de toString, toExponentialet toPrecision.

Pour arrondir un nombre, procédez comme suit:

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 si vous voulez une fonction « native », vous pouvez étendre le prototype:

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";

Cependant , gardez à l'esprit que polluer le prototype est considéré comme mauvais lorsque vous écrivez un module, car les modules ne devraient pas avoir d'effets secondaires. Donc, pour un module, utilisez la première fonction .

m93a
la source
12
Je pense que c'est la meilleure réponse. Cela évite la conversion de type. Sauce incroyable!
Phil
1
Très bonne réponse! Cependant ... je fais du JavaScript depuis environ 20 ans, mais je ne comprends pas pourquoi vous utilisez cette construction + (...) autour de la valeur de retour? Merci @sam de l'avoir frotté :) Comme je ne suis jamais trop vieux pour apprendre, veuillez préciser :-)
HammerNL
1
@HammerNL Malgré la conviction de Sam, il ne fait réellement rien :) C'est juste une pratique - cela permet aux IDE de reconnaître cette fonction comme type Number. Le fait est que +(anyValue)renvoie toujours un nombre - par exemple. +("45")revient 45, +(new Number(42))revient 42. C'est un peu comme si vous tapiez fort la fonction. Si vous en prenez l'habitude, vous pouvez éviter beaucoup de bugs :)
m93a
Pourquoi n'est-il pas intégré au noyau javascript: s
webmaster
2
Le résultat someNumber = Math.round( 42.008 * 1e2 ) / 1e2;n'est pas 42.01, il est ~42.0099999999999980. Raison: Le nombre 42.01n'existe pas et est arrondi au nombre existant le plus proche. btw, éprouvez les numéros par toPrecision(18)pour l'imprimer avec tous les chiffres pertinents.
Wiimm
118

J'ai résolu ce problème en modifiant ceci:

someNumber = someNumber.toFixed(2)

...pour ça:

someNumber = +someNumber.toFixed(2);

Cependant, cela convertira le nombre en chaîne et l'analysera à nouveau, ce qui aura un impact significatif sur les performances. Si vous vous souciez des performances ou de la sécurité du type, vérifiez également les autres réponses.

Eve Juan
la source
39
Non non Non Non Non! Ne fais pas ça! La conversion de nombre en chaîne juste pour l'arrondir est une très mauvaise pratique ! Faites plutôt someNumber = Math.round(someNumber * 1e2) / 1e2! Voir ma réponse pour une manière plus générale.
m93a
@ m93a - pourquoi est-ce une si mauvaise pratique?
jczaplew
3
@jczaplew Parce que si vous le faites de cette façon, le nombre binaire 32 bits est converti en une chaîne, en utilisant 16 bits pour chaque putain de chiffre décimal ! (Au fait, stocker des nombres en UTF-16 n'est pas la chose la plus pratique que vous fassiez.) Et que la chaîne est reconvertie en un nombre à virgule flottante 32 bits. Chiffre par chiffre. (Si j'ignore tous les tests qui doivent être faits avant de choisir le bon algorithme d'analyse.) Et tout cela en vain étant donné que vous pouvez le faire en utilisant 3 opérations rapides sur le flotteur.
m93a
2
@ m93a donc c'est pour des raisons de performances? cela a-t-il réellement un impact notable sur les performances?
Sebastianb
2
@jczaplew, Parce que les chaînes sont (1) pratiquement lentes et (2) théoriquement incorrectes.
Pacerier
29

Pourquoi ne pas l'utiliser parseFloat?

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

Je l'ai résolu en le convertissant en nombre à l'aide de la Number()fonction JavaScript

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

Bien sûr, il renvoie une chaîne. Si vous vouliez arrondir la variable numérique, vous utiliseriez plutôt Math.round (). Le but de toFixed est de formater le nombre avec un nombre fixe de décimales à afficher à l'utilisateur .

Joël Coehoorn
la source
4

Vous pouvez simplement utiliser un «+» pour convertir le résultat en nombre.

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

Qu'attendriez-vous qu'il renvoie lorsqu'il est censé formater un nombre? Si vous avez un nombre, vous ne pouvez pas faire quoi que ce soit avec lui parce que, par exemple 2 == 2.0 == 2.00, il doit s'agir d'une chaîne.

Tomas Vana
la source
3

Pour fournir un exemple de pourquoi il doit s'agir d'une chaîne:

Si vous formatez 1. toFixed (2), vous obtiendrez «1,00».

Ce n'est pas la même chose que 1, car 1 n'a pas 2 décimales.


Je sais que JavaScript n'est pas exactement un langage de performance , mais il y a de fortes chances que vous obteniez de meilleures performances pour un arrondi si vous utilisez quelque chose comme: roundValue = Math.round (value * 100) * 0.01

Pyro
la source
2

Parce que son utilisation principale est d'afficher des nombres? Si vous souhaitez arrondir les nombres, utilisez Math.round()avec des facteurs appropriés.

Christoph
la source
mais il affiche NUMBERS donc ne devrait-il pas renvoyer un "nombre"?
Derek Adair
3
@Derek: Seulement de la manière dont '42'c'est un nombre ... ce qui n'est pas le cas. Le fait qu'une chaîne ne contienne que des chiffres n'en fait pas un nombre. Ce n'est pas PHP. :-P
Chris Jester-Young
lol. Ce n'est pas une chaîne qui contient un nombre ... C'est un nombre qui est passé à une méthode. La méthode prend un nombre et renvoie une chaîne.
Derek Adair
@DerekAdair à droite mais un navigateur ne peut pas afficher un nombre, il affiche des chaînes, donc la conversion.
Nick M
1

Voici une version légèrement plus fonctionnelle de la réponse m93afournie.

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
la source
c'est une fonction pratique, pour le type indéfini cela ne fonctionne pas, j'ai ajouté du code pour ce cas; if (num! == undefined) {return + (Math.round (num * pow) / pow)} else {return 0; }
Dino Liu