Frage Auf maximal 2 Nachkommastellen runden (nur wenn nötig)


Ich möchte höchstens 2 Nachkommastellen runden, aber nur wenn nötig.

Eingang:

10
1.7777777
9.1

Ausgabe:

10
1.78
9.1

Wie kann ich das machen? JavaScript?


1835
2017-08-06 17:17


Ursprung


Antworten:


Benutzen Math.round(num * 100) / 100


2280
2017-08-06 17:20



Wenn der Wert ein Texttyp ist:

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

Wenn der Wert eine Zahl ist:

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

Es gibt einen Nachteil, dass Werte wie 1,5 "1,50" als Ausgabe ergeben. Eine von @minitech vorgeschlagene Lösung:

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.

Wie es scheint Math.round ist eine bessere Lösung. Aber es ist nicht! In einigen Fällen wird es NICHT richtig herum:

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

toFixed () wird auch NICHT in einigen Fällen korrekt (getestet in Chrome v.55.0.2883.87)!

Beispiele:

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.

Ich denke, das liegt daran, dass 1.555 eigentlich so etwas wie Float 1.55499994 hinter den Kulissen ist.

Lösung 1 Verwenden Sie ein Skript mit dem erforderlichen Rundungsalgorithmus, zum Beispiel:

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://plnr.co/edit/uau8BlS1cqbvWPCHJeOy?p=preview

Lösung 2 besteht darin, Front-End-Berechnungen zu vermeiden und abgerundete Werte vom Backend-Server abzurufen.


2319
2017-10-11 00:27



Sie können verwenden

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

Ich fand das hier weiter MDN. Ihr Weg vermeidet das Problem mit 1.005 erwähnt.

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

323
2017-08-21 12:56



MarkG's Antwort ist die richtige. Hier ist eine generische Erweiterung für beliebig viele Dezimalstellen.

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

Verwendung:

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

Gerätetest:

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

115
2017-11-01 07:40



Man kann verwenden .toFixed(NumberOfDecimalPlaces).

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

69
2017-10-21 17:02



Eine genaue Rundungsmethode. Quelle: 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);
        };
    }
})();

Beispiele:

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

54
2017-08-01 08:02



Keine der hier gefundenen Antworten ist korrekt. @stickycheeseman gebeten zu zusammenfassenSie alle rundeten die Zahl.

Verwenden Sie zum Abrunden Folgendes:

Math.ceil(num * 100)/100;

53
2018-06-13 09:35



Diese Frage ist kompliziert.

Angenommen, wir haben eine Funktion, roundTo2DP(num), die ein float als Argument verwendet und einen auf 2 Dezimalstellen gerundeten Wert zurückgibt. Was sollte jeder dieser Ausdrücke bewerten?

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

Die "offensichtliche" Antwort lautet, dass das erste Beispiel auf 0,01 runden sollte (weil es näher an 0,01 als an 0,02 liegt), während die anderen beiden auf 0,02 runden (weil 0,0150000000000000001 näher bei 0,02 als 0,01 liegt und weil 0,015 genau in der Mitte liegt) sie und es gibt eine mathematische Konvention, dass solche Zahlen aufgerundet werden).

Der Haken, den Sie vielleicht erraten haben, ist der roundTo2DP  kann nicht möglich sein implementiert werden, um diese offensichtlichen Antworten zu geben, weil alle drei Nummern daran übergeben werden die gleiche Nummer. IEEE 754 binäre Fließkommazahlen (die Art, die von JavaScript verwendet wird) können nicht genau die meisten nicht-ganzzahligen Zahlen darstellen, und so werden alle obigen drei numerischen Literale auf eine nahe gelegene gültige Fließkommazahl gerundet. Diese Nummer ist wie es ist genau

0.01499999999999999944488848768742172978818416595458984375

was näher bei 0,01 als bei 0,02 liegt.

Sie können sehen, dass alle drei Zahlen an Ihrer Browser-Konsole, Knoten-Shell oder einem anderen JavaScript-Interpreter identisch sind. Vergleichen Sie sie einfach:

> 0.014999999999999999 === 0.0150000000000000001
true

Also wenn ich schreibe m = 0.0150000000000000001, das genauer Wert von mmit dem ich am Ende komme ist näher dran 0.01 als es ist 0.02. Und trotzdem, wenn ich umziehe m zu einer Schnur ...

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

... bekomme ich 0.015, was auf 0.02 runden soll, und das ist merklich nicht die 56-stellige Zahl I sagte früher, dass alle diese Zahlen genau gleich waren. Also was für dunkle Magie ist das?

Die Antwort finden Sie in der ECMAScript-Spezifikation im Abschnitt 7.1.12.1: ToString, angewendet auf den Zahlentyp. Hier die Regeln zum Umwandeln einer Nummer m zu einer Schnur werden gelegt. Der Schlüsselteil ist Punkt 5, in dem eine ganze Zahl ist s generiert, dessen Ziffern in der String-Darstellung von verwendet werden m:

Lassen n, k, und s seien ganze Zahlen, so dass k ≥ 1, 10k-1 ≤ s <10k, der Zahlenwert für s × 10n-k ist m, und k ist so klein wie möglich. Beachten Sie, dass k die Anzahl der Ziffern in der Dezimaldarstellung von ist s, Das s ist nicht durch 10 teilbar, und das ist die niedrigste Ziffer von s ist nicht notwendigerweise eindeutig durch diese Kriterien bestimmt.

Der Schlüssel hier ist die Anforderung, dass "k ist so klein wie möglich. "Was diese Anforderung darstellt, ist eine Anforderung, die bei einer Nummer m, der Wert von String(m) haben müssen die geringstmögliche Anzahl von Ziffern während immer noch die Anforderung, dass Number(String(m)) === m. Da wissen wir das schon 0.015 === 0.0150000000000000001Jetzt ist klar, warum String(0.0150000000000000001) === '0.015' muss wahr sein.

Natürlich hat keine dieser Diskussionen direkt was beantwortet roundTo2DP(m)  sollte Rückkehr. Ob mder genaue Wert ist 0.01499999999999999944488848768742172978818416595458984375, aber seine String-Darstellung ist "0,015", was ist dann die richtig Antwort - mathematisch, praktisch, philosophisch oder was auch immer - wenn wir es auf zwei Dezimalstellen runden?

Es gibt keine einzige richtige Antwort darauf. Es hängt von Ihrem Anwendungsfall ab. Wahrscheinlich möchten Sie die String-Darstellung respektieren und nach oben abrunden, wenn:

  • Der dargestellte Wert ist von Natur aus diskret, z. ein Betrag in 3-stelliger Währung wie Dinar. In diesem Fall wahr Wert einer Zahl wie 0.015 ist 0.015, und die 0.0149999999 ... Darstellung, die es in binärem Gleitkomma erhält, ist ein Rundungsfehler. (Natürlich werden viele argumentieren, vernünftigerweise, dass Sie eine dezimale Bibliothek verwenden sollten, um solche Werte zu behandeln und sie niemals als binäre Fließkommazahlen darzustellen.)
  • Der Wert wurde von einem Benutzer eingegeben. In diesem Fall ist wiederum die genaue eingegebene Dezimalzahl "wahrer" als die nächste binäre Gleitkommadarstellung.

Auf der anderen Seite möchten Sie wahrscheinlich den binären Gleitkommawert berücksichtigen und nach unten abrunden, wenn Ihr Wert von einer inhärent kontinuierlichen Skala ist - zum Beispiel, wenn es sich um einen Messwert von einem Sensor handelt.

Diese beiden Ansätze erfordern einen anderen Code. Um die String-Repräsentation der Number zu respektieren, können wir (mit ziemlich etwas subtilem Code) unsere eigene Rundung implementieren, die direkt auf die String-Repräsentation wirkt, Ziffer für Zahl, mit dem gleichen Algorithmus, den Sie in der Schule verwendet hätten wurden unterrichtet, wie man Zahlen rundet. Es folgt ein Beispiel, das die Forderung des OP berücksichtigt, die Zahl auf 2 Dezimalstellen "nur wenn nötig" darzustellen, indem nach dem Komma nachgestellte Nullen entfernt werden; Sie müssen es natürlich an Ihre genauen Bedürfnisse anpassen.

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

Beispielverwendung:

> 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.014999999999999994444488848768742172978818416595458984375, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes ('0.014999999999999944488848768742172978818416595458984375', 2)
'0.01'

Die obige Funktion ist wahrscheinlich was Sie verwenden möchten, um zu vermeiden, dass Benutzer jemals Zahlen, die sie eingegeben haben, falsch gerundet sehen.

(Alternativ könntest du auch die Runde10 Bibliothek, die eine sich ähnlich verhaltende Funktion mit einer völlig anderen Implementierung bereitstellt.)

Aber was ist, wenn Sie die zweite Art von Zahl haben - einen Wert, der aus einer kontinuierlichen Skala entnommen wird, wo es keinen Grund zu der Annahme gibt, dass ungefähre dezimale Darstellungen mit weniger Nachkommastellen mehr sind genau als diejenigen mit mehr? In diesem Fall haben wir nicht möchte die String-Darstellung respektieren, da diese Darstellung (wie in der Spezifikation erklärt) bereits abgerundet ist; wir wollen nicht den Fehler machen, "0,014999999 ... 375 Runden bis 0,015 zu sagen, was auf 0,02 aufrundet, also 0,014999999 ... 375 Runden bis 0,02".

Hier können wir einfach das eingebaute verwenden toFixed Methode. Beachten Sie, dass Sie anrufen Number() auf der String von zurückgegeben toFixederhalten wir eine Zahl, deren String-Darstellung keine abschließenden Nullen enthält (dank der Art, wie JavaScript die String-Repräsentation einer Zahl berechnet, die weiter oben in dieser Antwort besprochen wurde).

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

53
2017-07-30 16:47



Erwägen .toFixed() und .toPrecision():

http://www.javascriptkit.com/javatutors/formatnummer.shtml


51
2017-08-06 17:21