Domanda Arrotondare al massimo a 2 cifre decimali (solo se necessario)


Mi piacerebbe arrotondare al massimo 2 posizioni decimali, ma solo se necessario.

Ingresso:

10
1.7777777
9.1

Produzione:

10
1.78
9.1

Come posso farlo in JavaScript?


1835
2017-08-06 17:17


origine


risposte:


Uso Math.round(num * 100) / 100


2278
2017-08-06 17:20



Se il valore è un tipo di testo:

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

Se il valore è un numero:

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

C'è un lato negativo che valori come 1.5 daranno "1.50" come output. Una correzione suggerita da @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.

Sembra che Math.round è una soluzione migliore. Ma non è! In alcuni casi lo farà NON arrotondare correttamente:

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

toFixed () lo farà anche NON arrotondare correttamente in alcuni casi (testato in Chrome v.55.0.2883.87)!

Esempi:

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.

Immagino, questo è perché 1.555 è in realtà qualcosa come float 1.55499994 dietro le quinte.

Soluzione 1 è quello di utilizzare uno script con l'algoritmo di arrotondamento richiesto, ad esempio:

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

Soluzione 2 è quello di evitare calcoli front-end e tirare valori arrotondati dal server di back-end.


2318
2017-10-11 00:27



Puoi usare

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

Ho trovato questo su MDN. La loro strada evita il problema con 1.005 che era menzionato.

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

322
2017-08-21 12:56



La risposta di MarkG è corretta. Ecco un'estensione generica per qualsiasi numero di posizioni decimali.

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

Uso:

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

Test unitario:

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

114
2017-11-01 07:40



Si può usare .toFixed(NumberOfDecimalPlaces).

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

68
2017-10-21 17:02



Un preciso metodo di arrotondamento. 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);
        };
    }
})();

Esempi:

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

53
2017-08-01 08:02



Nessuna delle risposte trovate qui è corretta. @stinkycheeseman ha chiesto di arrotondare, hai arrotondato il numero.

Per arrotondare, usa questo:

Math.ceil(num * 100)/100;

52
2018-06-13 09:35



Questa domanda è complicata.

Supponiamo di avere una funzione, roundTo2DP(num), che prende un float come argomento e restituisce un valore arrotondato a 2 posizioni decimali. A cosa dovrebbe valere ciascuna di queste espressioni?

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

La risposta "ovvia" è che il primo esempio dovrebbe arrotondare a 0,01 (perché è più vicino a 0,01 che a 0,02) mentre gli altri due dovrebbero arrotondare a 0,02 (perché 0,0150000000000000001 è più vicino a 0,02 che a 0,01 e perché 0,015 è esattamente a metà strada tra loro e c'è una convenzione matematica che tali numeri vengono arrotondati).

Il trucco, che potresti aver indovinato, è quello roundTo2DP  non può possibilmente essere implementato per dare quelle risposte ovvie, perché tutti e tre i numeri sono passati ad esso lo stesso numero. I numeri in virgola mobile binari IEEE 754 (il tipo utilizzato da JavaScript) non possono rappresentare esattamente la maggior parte dei numeri non interi, quindi tutti i tre valori letterali numerici sopra riportati vengono arrotondati a un numero in virgola mobile valido nelle vicinanze. Questo numero, come succede, lo è Esattamente

0,01499999999999999944488848768742172978818416595458984375

che è più vicino a 0,01 che a 0,02.

Puoi vedere che tutti e tre i numeri sono uguali nella console del browser, nella shell del nodo o in altri interpreti JavaScript. Basta confrontarli:

> 0,014999999999999999 === 0,0150000000000000001
true

Quindi quando scrivo m = 0.0150000000000000001, il valore esatto di ma cui finisco è più vicino 0.01 di quello che è 0.02. Eppure, se mi converto m a una stringa ...

> var m = 0,0150000000000000001;
> console.log (String (m));
0.015
> var m = 0,014999999999999999;
> console.log (String (m));
0.015

... Ottengo 0,015, che dovrebbe arrotondare a 0,02 e che è notevolmente non il numero di 56 decimali che ho detto prima diceva che tutti questi numeri erano esattamente uguali a. Quindi cos'è la magia oscura?

La risposta può essere trovata nella specifica ECMAScript, nella sezione 7.1.12.1: ToString applicato al tipo Numero. Ecco le regole per convertire un numero m a una stringa sono stabilite. La parte fondamentale è il punto 5, in cui un numero intero S viene generato le cui cifre verranno utilizzate nella rappresentazione String di m:

permettere n, K, e S essere interi tali che K ≥ 1, 10K-1 ≤ S <10K, il valore Numero per S × 10n-K è m, e K è il più piccolo possibile Si noti che k è il numero di cifre nella rappresentazione decimale di S, quella S non è divisibile per 10, e che la cifra meno significativa di S non è necessariamente determinato in modo univoco da questi criteri.

La parte chiave qui è il requisito che "K è il più piccolo possibile. "Ciò a cui questo requisito corrisponde è un requisito che, dato un numero m, il valore di String(m) deve avere il minor numero possibile di cifre pur soddisfacendo il requisito che Number(String(m)) === m. Visto che lo sappiamo già 0.015 === 0.0150000000000000001, ora è chiaro il perché String(0.0150000000000000001) === '0.015' deve essere vero

Naturalmente, nessuna di queste discussioni ha risposto direttamente a cosa roundTo2DP(m)  dovrebbero ritorno. Se mIl valore esatto è 0,01499999999999999944488848768742172978818416595458984375, ma la sua rappresentazione in stringa è '0,015', quindi qual è il corretta risposta - matematicamente, praticamente, filosoficamente o altro - quando lo arrotondiamo a due cifre decimali?

Non esiste un'unica risposta corretta a questo. Dipende dal tuo caso d'uso. Probabilmente vorresti rispettare la rappresentazione String e arrotondare verso l'alto quando:

  • Il valore rappresentato è intrinsecamente discreto, ad es. una quantità di valuta in una valuta di 3 decimali come dinari. In questo caso, il vero valore di un numero come 0,015 è 0,015 e la rappresentazione 0,0149999999 ... che ottiene in virgola mobile binario è un errore di arrotondamento. (Naturalmente, molti sostengono, ragionevolmente, che si dovrebbe usare una libreria decimale per gestire tali valori e non rappresentarli mai come numeri in virgola mobile binari in primo luogo.)
  • Il valore è stato digitato da un utente. In questo caso, di nuovo, il numero decimale esatto inserito è più "vero" della rappresentazione in virgola mobile binario più vicina.

D'altra parte, probabilmente si desidera rispettare il valore in virgola mobile binario e arrotondare verso il basso quando il valore è da una scala intrinsecamente continua, ad esempio, se si tratta di una lettura da un sensore.

Questi due approcci richiedono un codice diverso. Per rispettare la rappresentazione String del Numero, possiamo (con un po 'di codice ragionevolmente sottile) implementare il nostro arrotondamento che agisce direttamente sulla rappresentazione String, cifra per cifra, usando lo stesso algoritmo che avresti usato a scuola quando è stato insegnato come arrotondare i numeri. Di seguito è riportato un esempio che rispetta il requisito dell'OP di rappresentare il numero in 2 posizioni decimali "solo quando necessario" eliminando gli zero finali dopo il punto decimale; puoi, naturalmente, aver bisogno di adattarlo alle tue esigenze precise.

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

Esempio di utilizzo:

> 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 funzione sopra è probabilmente ciò che si desidera utilizzare per evitare che gli utenti abbiano mai visto erroneamente i numeri che hanno inserito.

(In alternativa, puoi anche provare il round10 libreria che fornisce una funzione che si comporta in modo simile con un'implementazione molto diversa.)

Ma cosa succede se si ha il secondo tipo di numero - un valore preso da una scala continua, dove non c'è motivo di pensare che rappresentazioni approssimative decimali con meno cifre decimali siano più preciso di quelli con più? In tal caso, noi non voglio rispettare la rappresentazione di stringa, perché quella rappresentazione (come spiegato nella specifica) è già sorta di arrotondamento; non vogliamo commettere l'errore di dire "0,014999999 ... 375 round fino a 0,015, che arrotonda fino a 0,02, quindi 0,014999999 ... 375 round fino a 0,02".

Qui possiamo semplicemente usare il built-in toFixed metodo. Si noti che chiamando Number() sulla stringa restituita da toFixed, otteniamo un numero la cui rappresentazione di stringa non ha zero finali (grazie al modo in cui JavaScript calcola la rappresentazione in stringa di un numero, discussa in precedenza in questa risposta).

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

52
2017-07-30 16:47