Javascript: formatage d'un nombre arrondi à N décimales

104

en JavaScript, la manière typique d'arrondir un nombre à N décimales est quelque chose comme:

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

Cependant, cette approche arrondira à un maximum de N décimales alors que je veux toujours arrondir à N décimales. Par exemple, «2,0» serait arrondi à «2».

Des idées?

hoju
la source
9
normalement, vous pouvez utiliser toFixed()( developer.mozilla.org/En/Core_JavaScript_1.5_Reference/… ), mais c'est bogué dans IE: stackoverflow.com/questions/661562/… ; vous devrez écrire votre propre version ...
Christoph
1
@hoju - peut-être changer la réponse acceptée - La réponse de David est correcte pour IE8 +, alors que la réponse acceptée présente de sérieux bogues sur tous les navigateurs.
robocat
@robocat: Êtes-vous sérieux?
Guffa

Réponses:

25

Ce n'est pas un problème d'arrondi, c'est un problème d'affichage. Un nombre ne contient pas d'informations sur les chiffres significatifs; la valeur 2 est la même que 2.0000000000000. C'est lorsque vous transformez la valeur arrondie en une chaîne que vous lui faites afficher un certain nombre de chiffres.

Vous pouvez simplement ajouter des zéros après le nombre, quelque chose comme:

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

(Notez que cela suppose que les paramètres régionaux du client utilisent le point comme séparateur décimal, le code a besoin de plus de travail pour fonctionner pour d'autres paramètres.)

Guffa
la source
3
toFixed est bogué..par exemple la décimale: 1.02449999998 si vous faites dec.toFixed (4) => 1.0244. Cela aurait dû être 1.0245 à la place.
Bat_Programmer
2
@deepeshk Mais quel serait le problème avec l'utilisation toFixed()du remplissage des décimales à la fin, après arrondi?
pauloya
10
@deepeshk dans quel navigateur? Je viens de l'essayer dans Chrome 17 et 1.02449999998.toFixed(4)revient correctement 1.0245.
Matt Ball
3
@MarkTomlin: Alors il semble que vous ayez une chaîne au lieu d'un nombre.
Guffa
1
.toFixed () fonctionne à merveille dans les navigateurs modernes (j'ai testé Chrome, Firefox, IE11, IE8). @Bat_Programmer - veuillez préciser quels navigateurs vous pensez avoir ce bogue.
robocat
243

Je pense qu'il y a une approche plus simple à tout donné ici, et c'est la méthode Number.toFixed()déjà implémentée en JavaScript.

écrivez simplement:

var myNumber = 2;

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

etc...

David
la source
Où est mentionné, hoju? J'ai examiné d'autres réponses et je n'ai trouvé personne signalant que la fonction toFixed était boguée. Merci
David
@David: C'est mentionné dans un commentaire à la réponse, par exemple.
Guffa
13
attention: toFixed()renvoie une chaîne.
Jordan Arseno
2
@JordanArseno cela peut être corrigé en utilisant parseInt (myNumber.toFixed (intVar)); pour renvoyer une valeur entière, ou parseFloat (myNumber.toFixed (floatVar)); pour renvoyer un flottant si l'utilisateur a des décimales.
Stephen Romero
50

J'ai trouvé un moyen. Voici le code de Christoph avec un correctif:

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

Lisez les détails de la répétition d'un caractère en utilisant un constructeur de tableau ici si vous êtes curieux de savoir pourquoi j'ai ajouté le "+ 1".

Elias Zamaria
la source
Passer une valeur de 708,3333333333333 avec une précision de 2 ou 3 se traduit par une valeur de retour de 708,00 pour moi. J'en ai besoin pour 2 décimales, dans Chrome et IE 9.toFixed (2) a répondu à mes besoins.
alan le
ce n'est pas une manière courante, par exemple, toFixed (16.775, 2) renvoie 16.77. Convertir un nombre en chaîne puis convertir est le seul moyen.
hiway
2
Il y a un bug avec cette méthode: toFixed (-0.1111, 2) renvoie 0.11, c'est-à-dire que le signe négatif est perdu.
mat
Fonctionne très bien, sauf que j'ai ajouté parseFloat (résultat) à la fin. Vaut le détour.
Artur Barseghyan
16

Il y a toujours une meilleure façon de faire les choses.

var number = 51.93999999999761;

Je voudrais obtenir une précision à quatre chiffres: 51,94

fais juste:

number.toPrecision(4);

le résultat sera: 51,94

de.la.ru
la source
2
Et si vous ajoutez 100? Avez-vous besoin de le changer en number.toPrecision (5)?
JohnnyBizzle
2
Oui. 31.939383.toPrecision (4)> "31.94" / 131.939383.toPrecision (4)> "131.9"
ReSpawN
6

Méthode d'arrondi de type PHP

Le code ci-dessous peut être utilisé pour ajouter votre propre version de Math.round à votre propre espace de noms qui prend un paramètre de précision. Contrairement à l'arrondi décimal dans l'exemple ci-dessus, cela n'effectue aucune conversion vers et à partir de chaînes, et le paramètre de précision fonctionne de la même manière que PHP et Excel où un 1 positif arrondirait à 1 décimale et -1 arrondirait aux dizaines.

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

à partir de la référence Mozilla Developer pour Math.round ()

JohnnyBizzle
la source
2

J'espère que le code fonctionne (n'a pas fait beaucoup de tests):

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
la source
Votre code a un bogue. J'ai essayé toFixed (2.01, 4) et j'ai obtenu un résultat de "2.100". Si jamais je trouve un moyen de le réparer, je le posterai comme réponse à cette question.
Elias Zamaria
@ mikez302: le calcul du remplissage était décalé de un; devrait fonctionner maintenant, mais n'hésitez pas à me déranger à nouveau s'il est toujours cassé ...
Christoph
Très étrange. Lorsque je lance cette fonction avec la console Firebug ouverte dans Firefox 17, cela gèle tout le navigateur comme si js est pris dans une boucle sans fin. Même si je ne console.log pas la sortie. Si je n'ai pas activé firebug, le bogue ne se produit pas.
SublymeRick
1
Mise à jour, je l'ai exécuté dans le chrome et j'obtiens: Uncaught RangeError: La taille maximale de la pile d'appel est dépassée en ce qui concerne l'appel de fonction toFixed.
SublymeRick
@SublymeRick: Je n'ai aucune idée de pourquoi cela se produit; tourné dans le noir: essayez de renommer la fonction ...
Christoph
2

Je pense que la fonction ci-dessous peut aider

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));
}

utilisation: roundOff(600.23458,2);retournera600.23

KRISHNA TEJA
la source
2

Cela fonctionne pour arrondir à N chiffres (si vous voulez simplement tronquer à N chiffres, supprimez l'appel Math.round et utilisez celui Math.trunc):

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

J'ai dû recourir à une telle logique à Java dans le passé lorsque je créais des composants E-Slate de manipulation de données . C'est depuis que j'ai découvert qu'en ajoutant 0,1 plusieurs fois à 0, vous vous retrouveriez avec une partie décimale étonnamment longue (cela est dû à l'arithmétique en virgule flottante).

Un commentaire utilisateur au format numéro pour toujours afficher 2 décimales appelle cette technique de mise à l'échelle.

Certains mentionnent qu'il y a des cas qui ne sont pas arrondis comme prévu et à http://www.jacklmoore.com/notes/rounding-in-javascript/, c'est suggéré à la place:

function round(value, decimals) {
  return Number(Math.round(value+'e'+decimals)+'e-'+decimals);
}
George Birbilis
la source
btw, POSITS est prometteur pour les futures architectures h / w: nextplatform.com/2019/07/08
George Birbilis
-2

Si vous ne vous souciez pas vraiment de l'arrondi, ajoutez simplement un toFixed (x), puis supprimez les 0 de fin et le point si nécessaire. Ce n'est pas une solution rapide.

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
la source
J'ai essayé format(1.2, 5)et obtenu 1.2, alors que je m'y attendais 1.20000.
Elias Zamaria
Désolé @EliasZamaria, je n'ai pas compris au début. J'ai édité le message. Notez simplement qu'il renverra "0.0000" lorsque la valeur n'est pas définie.
Juan Carrey