javascript supérieur - Arrondir à au plus 2 décimales(seulement si nécessaire)




inférieur jquery (25)

J'aimerais arrondir au maximum 2 décimales, mais uniquement si nécessaire .

Contribution:

10
1.7777777
9.1

Sortie:

10
1.78
9.1

Comment puis-je faire cela en JavaScript ?


Answers

Pour moi, MDN ne donnait pas la bonne réponse. J'ai trouvé toFixed fonctionne mieux. Voici des exemples des deux:

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

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


Manière la plus simple:

+num.toFixed(2)

Il le convertit en chaîne, puis en un entier / float.



En général, l’arrondi se fait en mettant à l’échelle: round(num / p) * p

L'utilisation de la notation exponentielle gère correctement l'arrondi des nombres + v. Cependant, cette méthode ne parvient pas à arrondir correctement les cas marginaux.

function round(num, precision = 2) {
	var scaled = Math.round(num + "e" + precision);
	return Number(scaled + "e" + -precision);
}

// testing some edge cases
console.log( round(1.005, 2) );  // 1.01 correct
console.log( round(2.175, 2) );  // 2.18 correct
console.log( round(5.015, 2) );  // 5.02 correct

console.log( round(-1.005, 2) );  // -1    wrong
console.log( round(-2.175, 2) );  // -2.17 wrong
console.log( round(-5.015, 2) );  // -5.01 wrong

Voici également une fonction que j’ai écrite pour effectuer correctement les arrondis arithmétiques. Vous pouvez le tester vous-même.

/**
 * MidpointRounding away from zero ('arithmetic' rounding)
 * Uses a half-epsilon for correction. (This offsets IEEE-754
 * half-to-even rounding that was applied at the edge cases).
 */

function RoundCorrect(num, precision = 2) {
	// half epsilon to correct edge cases.
	var c = 0.5 * Number.EPSILON * num;
//	var p = Math.pow(10, precision); //slow
	var p = 1; while (precision--> 0) p *= 10;
	if (num < 0)
		p *= -1;
	return Math.round((num + c) * p) / p;
}

// testing some edge cases
console.log(RoundCorrect(1.005, 2));  // 1.01 correct
console.log(RoundCorrect(2.175, 2));  // 2.18 correct
console.log(RoundCorrect(5.015, 2));  // 5.02 correct

console.log(RoundCorrect(-1.005, 2));  // -1.01 correct
console.log(RoundCorrect(-2.175, 2));  // -2.18 correct
console.log(RoundCorrect(-5.015, 2));  // -5.02 correct


Cette question est compliquée.

Supposons que nous ayons une fonction, roundTo2DP(num) , qui prend un float comme argument et renvoie une valeur arrondie à 2 décimales. Que doit évaluer chacune de ces expressions?

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

La réponse «évidente» est que le premier exemple devrait arrondir à 0,01 (car il est plus proche de 0,01 que 0,02), tandis que les deux autres devraient arrondir à 0,02 (parce que 0,0150000000000000001 est plus proche de 0,02 que 0,01 et que 0,015 est exactement à mi-chemin entre et il existe une convention mathématique selon laquelle ces nombres sont arrondis).

Le problème, que vous avez peut-être deviné, est que roundTo2DP ne peut pas être implémenté pour donner ces réponses évidentes, car les trois nombres qui lui ont été transmis sont identiques . Les nombres à virgule flottante binaires IEEE 754 (le type utilisé par JavaScript) ne peuvent pas représenter exactement la plupart des nombres non entiers. Par conséquent, les trois littéraux numériques ci-dessus sont arrondis à un nombre à virgule flottante valide proche. Ce nombre, comme il arrive, est exactement

0.0149999999999999999944488848768742172978818416595458984375

qui est plus proche de 0,01 que de 0,02.

Vous pouvez constater que les trois numéros sont identiques sur la console de votre navigateur, le shell Node ou un autre interpréteur JavaScript. Il suffit de les comparer:

> 0.014999999999999999 === 0.0150000000000000001
true

Donc, quand j'écris m = 0.0150000000000000001 , la valeur exacte de m laquelle je me retrouve est plus proche de 0.01 que de 0.02 . Et pourtant, si je convertis m en String ...

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

... J'obtiens 0,015, ce qui devrait arrondir à 0,02, ce qui n'est visiblement pas le nombre à 56 décimales que j'ai déjà indiqué plus haut. Tous ces chiffres étaient exactement égaux à. Alors, quelle magie noire est-ce?

La réponse se trouve dans la spécification ECMAScript, dans la section 7.1.12.1: ToString appliquée au type Number . Ici, les règles pour convertir un nombre m en chaîne sont définies. La partie clé est le point 5, dans lequel est généré un entier s dont les chiffres seront utilisés dans la représentation sous forme de chaîne de m :

Soit n , k et s des entiers tels que k ≥ 1, 10 k -1s <10 k , la valeur Nombre pour s × 10 n - k est m et k est aussi petit que possible. Notez que k est le nombre de chiffres dans la représentation décimale de s , que s n'est pas divisible par 10 et que le chiffre le moins significatif de s n'est pas nécessairement déterminé uniquement par ces critères.

L'élément clé ici est l'exigence selon laquelle " k est le plus petit possible". Cela correspond à une exigence selon laquelle, étant donné le nombre m , la valeur de la String(m) doit comporter le moins de chiffres possible tout en satisfaisant l'exigence que Number(String(m)) === m . Puisque nous savons déjà que 0.015 === 0.0150000000000000001 , nous 0.015 === 0.0150000000000000001 maintenant pourquoi String(0.0150000000000000001) === '0.015' doit être vrai.

Bien entendu, aucune partie de cette discussion n'a directement répondu à ce que roundTo2DP(m) devrait être renvoyé. Si la valeur exacte de m est 0.0149999999999999999484848768742172978818416595458984375, mais que sa représentation en chaîne est '0.015', quelle est la bonne réponse - mathématiquement, pratiquement, philosophiquement ou peu importe - quand on arrondit à deux décimales?

Il n'y a pas une seule bonne réponse à cela. Cela dépend de votre cas d'utilisation. Vous voulez probablement respecter la représentation des chaînes et arrondir à la hausse lorsque:

  • La valeur représentée est intrinsèquement discrète, par exemple une quantité de devise dans une devise à 3 décimales, comme des dinars. Dans ce cas, la valeur vraie d'un nombre tel que 0.015 est 0.015 et la représentation 0.0149999999 ... qu'il obtient en virgule flottante binaire est une erreur d'arrondi. (Bien entendu, nombreux seront ceux qui argueront, raisonnablement, qu'il est préférable d'utiliser une bibliothèque décimale pour gérer ces valeurs et de ne jamais les représenter sous la forme de nombres à virgule flottante binaire.)
  • La valeur a été saisie par un utilisateur. Dans ce cas encore, le nombre décimal exact entré est plus vrai que la représentation en virgule flottante binaire la plus proche.

D'autre part, vous voudrez probablement respecter la valeur en virgule flottante binaire et arrondir vers le bas lorsque votre valeur provient d'une échelle intrinsèquement continue - par exemple, s'il s'agit d'une lecture d'un capteur.

Ces deux approches nécessitent un code différent. Pour respecter la représentation du nombre sous forme de chaîne, nous pouvons (avec un code assez subtil) implémenter notre propre arrondi qui agit directement sur la représentation sous forme de chaîne, chiffre par chiffre, en utilisant le même algorithme que celui que vous auriez utilisé à l'école. ont appris à arrondir les nombres. Vous trouverez ci-dessous un exemple qui respecte l'exigence de l'OP de représenter le nombre à 2 décimales "uniquement lorsque cela est nécessaire" en supprimant les zéros à la fin du point décimal; Bien entendu, vous devrez peut-être adapter le système à vos besoins précis.

/**
 * 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://.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+$/, '')
}

Exemple d'utilisation:

> 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'

La fonction ci-dessus est probablement ce que vous voulez utiliser pour éviter que les utilisateurs ne voient jamais les nombres entrés mal arrondis.

(Au lieu de cela, vous pouvez également essayer la bibliothèque github.com/jhohlfeld/round10 , qui fournit une fonction ayant un comportement similaire avec une implémentation extrêmement différente.)

Mais que se passe-t-il si vous avez le deuxième type de nombre - une valeur prise sur une échelle continue, où il n’ya aucune raison de penser que les représentations décimales approximatives avec moins de décimales sont plus précises que celles avec plus de nombres? Dans ce cas, nous ne voulons pas respecter la représentation String, car cette représentation (comme expliqué dans la spécification) est déjà en quelque sorte arrondie; nous ne voulons pas commettre l'erreur de dire "0,014999999 ... 375 arrondis à 0,015, ce qui arrondit à 0,02, donc 0,014999999 ... 375 arrondis à 0,02".

Ici, nous pouvons simplement utiliser la toFixed intégrée toFixed . Notez qu'en appelant Number() sur la chaîne renvoyée par toFixed , nous obtenons un nombre dont la représentation de chaîne n'a pas de zéros de fin (grâce à la manière dont JavaScript calcule la représentation sous forme de chaîne d'un nombre, décrite plus haut dans cette réponse).

/**
 * 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://.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);
}

Je vais ajouter une autre approche à cela.

number = 16.6666666;
console.log(parseFloat(number.toFixed(2)));
"16.67"

number = 16.6;
console.log(parseFloat(number.toFixed(2)));
"16.6"

number = 16;
console.log(parseFloat(number.toFixed(2)));
"16"

.toFixed(2)retourne une chaîne avec exactement 2 décimales, qui peuvent être ou ne pas être des zéros de fin. Faire un parseFloat()va éliminer ces zéros à la fin.


On peut utiliser .toFixed(NumberOfDecimalPlaces) .

var str = 10.234.toFixed(2); // => '10.23'
var number = Number(str); // => 10.23

La réponse de MarkG est la bonne. Voici une extension générique pour un nombre quelconque de décimales.

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

Usage:

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

Test de l'unité:

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

Une méthode d'arrondissement précise. Source: MDN

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

Exemples:

// 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

2017
Il suffit d'utiliser le code natif .toFixed()

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

Si vous devez être strict et ajouter des chiffres au besoin, vous pouvez utiliser

number = 1; // "1"
number.toFixed(5).replace(/\.?0*$/g,'');

Une manière plus simple de l’ES6 est de

const round = (x, n) => 
  parseFloat(Math.round(x * Math.pow(10, n)) / Math.pow(10, n)).toFixed(n);

Ce modèle renvoie également la précision demandée.

ex:

round(44.7826456, 4)  // yields 44.7826
round(78.12, 4)       // yields 78.1200

Vous pouvez utiliser

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

J'ai trouvé ceci sur MDN . Leur chemin évite le problème mentioned avec 1.005.

roundToTwo(1.005)
1.01
roundToTwo(10)
10
roundToTwo(1.7777777)
1.78
roundToTwo(9.1)
9.1
roundToTwo(1234.5678)
1234.57

var roundUpto = function(number, upto){
    return Number(number.toFixed(upto));
}
roundUpto(0.1464676, 2);

toFixed(2) Ici 2 est le nombre de chiffres jusqu'à lequel nous voulons arrondir ce nombre.


Voici une méthode prototype:

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

var yournum = 10.55555;
yournum = yournum.round(2);

Si vous utilisez la bibliothèque lodash, vous pouvez utiliser la méthode round de lodash comme suit.

_.round(number, precision)

Par exemple:

_.round(1.7777777, 2) = 1.78


Essayez cette solution légère :

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

 round(1.222,  2) ;
 // 1.22
 round(1.222, 10) ;
 // 1.222

Utilisez Math.round(num * 100) / 100



Il y a deux façons de le faire. Pour les gens comme moi, la variante de 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))
}

Usage:

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

Si votre projet utilise jQuery ou lodash, vous pouvez également trouver la roundméthode appropriée dans les bibliothèques.

Mise à jour 1

J'ai supprimé la variante n.toFixed(2), car ce n'est pas correct. Merci @ avalanche1


+(10).toFixed(2); // = 10
+(10.12345).toFixed(2); // = 10.12

(10).toFixed(2); // = 10.00
(10.12345).toFixed(2); // = 10.12

Pour ne pas traiter beaucoup de 0, utilisez cette variante:

Math.round(num * 1e2) / 1e2

Voici un moyen simple de le faire:

Math.round(value * 100) / 100

Vous voudrez peut-être aller de l'avant et créer une fonction distincte pour le faire pour vous cependant:

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

Ensuite, il vous suffirait de transmettre la valeur.

Vous pouvez l'améliorer pour arrondir à un nombre quelconque de décimales en ajoutant un deuxième paramètre.

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

    return (Math.round(value * multiplier) / multiplier);
}

MarkG et Lavamantis ont proposé une solution bien meilleure que celle qui a été acceptée. C'est dommage qu'ils ne reçoivent pas plus de votes positifs!

Voici la fonction que j'utilise pour résoudre les problèmes de nombres décimaux à virgule flottante également basés sur MDN . C'est encore plus générique (mais moins concis) que la solution 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));
}

Utilisez-le avec:

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

Par rapport à la solution de Lavamantis, nous pouvons faire ...

round(1234.5678, -2); // Returns 1200
round("123.45");      // Returns 123

Ce sont des réponses très bonnes mais longues.

Pour résumer mes expériences:

  1. Les contrôleurs et les fournisseurs (services, usines, etc.) servent à modifier le modèle de données, PAS HTML.
  2. HTML et les directives définissent la mise en page et la liaison au modèle.
  3. Si vous devez partager des données entre contrôleurs, créez un service ou une fabrique. Ce sont des singletons partagés dans l'application.
  4. Si vous avez besoin d'un widget HTML, créez une directive.
  5. Si vous avez des données et que vous essayez maintenant de mettre à jour le HTML ... STOP! mettez à jour le modèle et assurez-vous que votre code HTML est lié au modèle.






javascript decimal rounding decimal-point