Auf höchstens 2 Dezimalstellen runden (nur bei Bedarf)


2758

Ich möchte höchstens 2 Dezimalstellen runden, aber nur bei Bedarf .

Eingang:

10
1.7777777
9.1

Ausgabe:

10
1.78
9.1

Wie kann ich das in JavaScript machen?


22
Ich habe eine Geige mit vielen der Techniken gemacht, die hier als Lösungen angeboten werden ... also können Sie vergleichen: Geige
dsdsdsdsd

37
Niemand scheint sich dessen bewusst zu sein Number.EPSILON. Verwenden Sie Math.round( num * 100 + Number.EPSILON ) / 100.
Cronvel

3
Für neue Leser ist dies nur möglich, wenn Sie ein Ergebnis vom Typ Zeichenfolge haben . Gleitkomma-Mathematik auf binären internen Darstellungen von Zahlen bedeutet, dass es immer Zahlen gibt, die nicht als ordentliche Dezimalstellen dargestellt werden können.
Walf

9
@cronvel Kannst du hier den Grund für die Verwendung erklären Number.EPSILON?
Bruce Sun

5
Ich bin in das Kaninchenloch gefallen und habe einige der interessanteren Antworten auf dieser Seite getestet (nur erste Seite). Hier ist ein Codepen . Hinweis: Je mehr positive Stimmen die Antwort hat, desto geringer sind die Chancen, dass sie richtig funktioniert.
Adam Jagosz

Antworten:


3493

Verwenden Math.round(num * 100) / 100

Bearbeiten: Um sicherzustellen, dass Dinge wie 1.005 richtig sind, verwenden wir

Math.round((num + Number.EPSILON) * 100) / 100


395
Während dies in den meisten Fällen funktioniert, funktioniert es nicht für 1.005, was sich als 1 statt 1.01 herausstellt
James

83
@ James Wow, das ist wirklich komisch. Ich arbeite in der Chrome-Entwicklungskonsole und bemerke, dass 1,005 * 100 = 100,49999999999999. Math.round (100.49999999999999) ergibt 100, während Math.round (100.5) 101 ergibt. IE9 macht dasselbe. Dies ist auf Gleitkomma-Verrücktheit in Javascript
Stinkycheeseman

153
Eine einfache Problemumgehung. Verwenden Sie für 2 dp Math.round((num + 0.00001) * 100) / 100. Versuchen Sie Math.round((1.005 + 0.00001) * 100) / 100undMath.round((1.0049 + 0.00001) * 100) / 100
mrkschan

31
@mrkschan Warum funktioniert das und ist das für alle Zahlen kinderleicht?
CMCDragonkai

86
Für diejenigen unter Ihnen, die es nicht verstehen, wird diese Technik Skalierung genannt. Grundsätzlich lautet die Antwort hier, zwei Ziffern über den Dezimalpunkt zu bringen und die Ziffer in eine ganze Zahl umzuwandeln, um alle verrückten Gleitkommaprobleme zu vermeiden. Runden Sie diese Zahl ab und übersetzen Sie sie dann wieder in das, was sie zuvor war, indem Sie sie durch 100 teilen, und Sie haben Ihre Antwort auf 2dp.
Alex_Nabu

3064

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. Ein von @minitech vorgeschlagener Fix:

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.

Es scheint Math.roundeine bessere Lösung zu sein. Aber es ist nicht! In einigen Fällen wird es NICHT richtig gerundet:

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

toFixed () rundet in einigen Fällen auch NICHT richtig (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 tatsächlich so etwas wie Float 1.55499994 hinter den Kulissen ist.

Lösung 1 besteht darin, ein Skript mit dem erforderlichen Rundungsalgorithmus zu verwenden, 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://plnkr.co/edit/uau8BlS1cqbvWPCHJeOy?p=preview

HINWEIS: Dies ist keine universelle Lösung für alle. Es gibt verschiedene Rundungsalgorithmen. Ihre Implementierung kann je nach Ihren Anforderungen unterschiedlich sein. https://en.wikipedia.org/wiki/Rounding

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


81
Dieser eine (der toFixed) Ansatz ist gut und hat für mich funktioniert, entspricht aber insbesondere nicht der ursprünglichen Anforderung "nur bei Bedarf". (Es rundet 1,5 bis 1,50, was die Spezifikation bricht.)
Per Lundberg

29
Für die "wenn nötig" -Anforderung tun Sie dies: parseFloat(number.toFixed(decimalPlaces)); @PerLundberg
Onur Yıldırım

36
parseFloat("55.555").toFixed(2)kehrt "55.55"in der Chrome-Entwicklungskonsole zurück.
Levi Botelho

22
Es gibt keinen Vorteil, toFixed anstelle von Math.round zu verwenden. toFixed führt zu den gleichen Rundungsproblemen (versuchen Sie es mit 5.555 und 1.005), ist aber ungefähr 500x (kein Scherz) langsamer als Math.round ... Scheint, als ob die @ MarkG-Antwort hier genauer ist.
Pierre

17
toFixed gibt "manchmal" keine Zeichenfolge zurück, sondern immer eine Zeichenfolge.
McGuireV10

464

Sie können verwenden

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

Ich fand das vorbei MDN gefunden . Ihr Weg vermeidet das erwähnte Problem mit 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

13
@Redsandro +(val)ist das Zwangsäquivalent zur Verwendung Number(val). Das Verketten von "e-2" mit einer Zahl führte zu einer Zeichenfolge, die wieder in eine Zahl umgewandelt werden musste.
Jack

41
Beachten Sie, dass bei großen und kleinen Schwimmern, die NaN produzieren würden, z. B. + "1e-21 + 2" nicht korrekt analysiert werden.
Pierre

16
Sie haben das 1.005-Problem "gelöst", aber ein neues eingeführt: Jetzt wird in der Chrome-Konsole roundToTwo(1.0049999999999999)(seitdem 1.0049999999999999 == 1.005) zwangsläufig 1.01 angezeigt . Es scheint mir, dass der Float, den Sie erhalten, wenn Sie num = 1.005"offensichtlich" eingeben, auf 1,00 runden sollte, da der genaue Wert von num weniger als 1,005 beträgt. Natürlich scheint es mir auch, dass die Zeichenfolge '1.005' 'offensichtlich' 'auf 1.01 gerundet werden sollte. Die Tatsache, dass verschiedene Menschen unterschiedliche Intuitionen darüber zu haben scheinen, was das tatsächliche richtige Verhalten hier ist, ist ein Teil dessen, warum es kompliziert ist.
Mark Amery

35
Es gibt keine (Gleitkomma-) Zahl dazwischen 1.0049999999999999und 1.005per Definition sind sie also dieselbe Zahl. Dies wird als Dedekind-Schnitt bezeichnet.
Azmisov

6
@ Azmisov ist richtig. Während 1.00499 < 1.005ist true, 1.0049999999999999 < 1.005bewertet zu false.
Falconepl

146

MarkGs Antwort ist die richtige. Hier ist eine generische Erweiterung für eine beliebige Anzahl von Dezimalstellen.

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

Verwendungszweck:

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

20
Pierre warf ein ernstes Problem mit der Antwort von MarkG auf.
Dsjoerg

9
Hinweis: Wenn Sie den Number.prototype nicht ändern möchten, schreiben Sie dies einfach als Funktion: function round(number, decimals) { return +(Math.round(number + "e+" + decimals) + "e-" + decimals); }
Philipp Tsipman

2
Eine coole Art, dies zu erweitern. Sie können überprüfen, ob die Dezimalzahl negativ ist, und dann e + und e- invertieren. Dann ist n = 115; n.round (-2); würde 100 produzieren
Lee Louviere

3
Versuchen Sie n: 1e + 19 - es gibt NaN zurück
DavidJ

3
Dieser Algorithmus rundet immer auf (anstatt von Null weg). Bei negativen Zahlen entspricht die Ausgabe also nicht Ihren Erwartungen:(-1.005).round(2) === -1
Aleksej Komarov,

123

Du solltest benutzen:

Math.round( num * 100 + Number.EPSILON ) / 100

Niemand scheint sich dessen bewusst zu sein Number.EPSILON .

Es ist auch erwähnenswert, dass dies keine JavaScript-Verrücktheit ist, wie einige Leute sagten.

So funktionieren Gleitkommazahlen in einem Computer einfach. Wie 99% der Programmiersprachen verfügt JavaScript nicht über hausgemachte Gleitkommazahlen. Dafür ist die CPU / FPU erforderlich. Ein Computer verwendet Binärdateien, und in Binärdateien gibt es keine Zahlen wie0.1 , sondern nur eine binäre Näherung dafür. Warum? Aus dem gleichen Grund kann 1/3 nicht dezimal geschrieben werden: Der Wert ist 0,33333333 ... mit einer Unendlichkeit von drei.

Hier komm Number.EPSILON. Diese Zahl ist die Differenz zwischen 1 und der nächsten Zahl, die in den Gleitkommazahlen mit doppelter Genauigkeit vorhanden ist. Das war's: Es gibt keine Zahl zwischen 1und 1 + Number.EPSILON.

BEARBEITEN:

Lassen Sie uns, wie in den Kommentaren gefragt, eines klarstellen: Hinzufügen Number.EPSILON ist nur relevant, wenn der zu runde Wert das Ergebnis einer arithmetischen Operation ist, da es ein Gleitkomma-Fehlerdelta verschlucken kann.

Dies ist nicht sinnvoll, wenn der Wert aus einer direkten Quelle stammt (z. B. Literal, Benutzereingabe oder Sensor).

EDIT (2019):

Wie @maganap und einige Leute bereits betont haben, ist es am besten, Number.EPSILONvor dem Multiplizieren Folgendes hinzuzufügen :

Math.round( ( num + Number.EPSILON ) * 100 ) / 100

EDIT (Dezember 2019):

In letzter Zeit verwende ich eine ähnliche Funktion zum Vergleichen von epsilon-fähigen Zahlen:

const ESPILON_RATE = 1 + Number.EPSILON ;
const ESPILON_ZERO = Number.MIN_VALUE ;

function epsilonEquals( a , b ) {
  if ( Number.isNaN( a ) || Number.isNaN( b ) ) {
    return false ;
  }
  if ( a === 0 || b === 0 ) {
    return a <= b + EPSILON_ZERO && b <= a + EPSILON_ZERO ;
  }
  return a <= b * EPSILON_RATE && b <= a * EPSILON_RATE ;
}

Mein Anwendungsfall ist eine Behauptung + Datenvalidierungsbibliothek, die ich seit vielen Jahren entwickle.

In dem Code, den ich verwende, ESPILON_RATE = 1 + 4 * Number.EPSILONund EPSILON_ZERO = 4 * Number.MIN_VALUE(viermal das Epsilon), weil ich möchte, dass ein Gleichheitsprüfer locker genug ist, um Gleitkommafehler zu kumulieren.

Bisher sieht es für mich perfekt aus. Ich hoffe es wird helfen.


1
@ Palota Sie könnten eine wirklich kleine Zahl definieren, wie die EPSILON Cronvel erwähnt
Daniel San

3
Das ist die richtige Antwort! Das Problem selbst hängt damit zusammen, wie Float-Zahlen intern funktionieren, nicht mit Javascript
Daniel San,

22
Tatsächlich müssen Sie epsilon hinzufügen, bevor Sie mit 100 multiplizieren Math.round( (num + Number.EPSILON) * 100) / 100. Ich stimme auch zu, dass dies die richtige Methode zum korrekten Runden ist (obwohl es nicht genau das ist, was in dieser Frage gestellt wurde).
Maganap

2
@cronvel Hmm. Du hast recht; Die Wahl der Richtung macht Sinn. Mein verbleibender Einwand ist also, dass dies nur dann sinnvoll ist, wenn Sie in einer Domäne arbeiten, in der Sie einen grundsätzlichen Grund zu der Annahme haben, dass Ihr Wert eine "runde Zahl" ist. Wenn Sie wissen, dass Ihre Eingabe das Ergebnis einfacher arithmetischer Operationen für Zahlen mit einer kleinen Anzahl von Dezimalstellen ist, dann haben Sie wahrscheinlich nur 0.004999999999999999das Ergebnis eines zusammengesetzten Gleitkommafehlers und das mathematisch korrekte Ergebnis war wahrscheinlich 0,005. Wenn es ein Messwert von einem Sensor ist? Nicht so viel.
Mark Amery

1
@marchaos Es ist nicht aufhöre Hälften werden immer gerundet auf . ZB Math.round(1.5)= 2, aber Math.round(-1.5)= -1. Das ist also vollkommen konsistent. Hier ist -1 größer als -2, genau wie -1000 größer als -1000,01 ist. Nicht zu verwechseln mit größer absoluten Zahlen.
Cronvel

84

Man kann verwenden .toFixed(NumberOfDecimalPlaces).

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

4
Auch weil es nachgestellte Nullen hinzufügt, was in der ursprünglichen Frage nicht der Fall war .
Alastair Maw

2
Nachgestellte Nullen können jedoch leicht mit einem regulären Ausdruck entfernt werden, dh `Number (10.10000.toFixed (2) .replace (/ 0 + $ /, ''))` => 10.1
Chad

1
@ Daniel die Top-Antwort die gleiche ist (wie jetzt) , aber es nicht alsways Runde richtig, versuchen Sie, +(1.005).toFixed(2)welche Renditen 1statt 1.01.
Emile Bergeron

3
@ChadMcElligott: Ihre Regex funktioniert nicht gut mit ganzen Zahlen: Number(9).toFixed(2).replace(/0+$/, '')=> "9."
Jacob van Lingen

Es scheint, dass dies nicht rund ist. Es ist im Grunde nur abgeschnitten. In einigen Fällen gut genug! Vielen Dank.
Iedmrc

78

Diese Frage ist kompliziert.

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

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

Die 'offensichtliche' Antwort ist, 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 sollten (weil 0,0150000000000000001 näher an 0,02 als an 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, dass er roundTo2DP möglicherweise nicht implementiert werden kann, um diese offensichtlichen Antworten zu geben, da alle drei an ihn übergebenen Zahlen dieselbe Zahl sind . IEEE 754-Binärgleitkommazahlen (die von JavaScript verwendete Art) können die meisten nicht ganzzahligen Zahlen nicht genau darstellen. Daher werden alle drei oben genannten numerischen Literale auf eine nahegelegene gültige Gleitkommazahl gerundet. Diese Zahl ist zufällig genau

0.01499999999999999944488848768742172978818416595458984375

das ist näher an 0,01 als an 0,02.

Sie können sehen, dass alle drei Zahlen in Ihrer Browserkonsole, Node-Shell oder einem anderen JavaScript-Interpreter gleich sind. Vergleichen Sie sie einfach:

> 0.014999999999999999 === 0.0150000000000000001
true

Wenn ich also schreibe m = 0.0150000000000000001, ist der genaue Wertm , mit dem ich ende, näher 0.01als er ist 0.02. Und doch, wenn ich min einen String konvertiere ...

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

... Ich bekomme 0,015, was auf 0,02 runden sollte und was merklich nicht die 56-Dezimalstellen-Zahl ist, von der ich zuvor gesagt habe, dass alle diese Zahlen genau gleich sind. Was für eine dunkle Magie ist das?

Die Antwort finden Sie in der ECMAScript-Spezifikation in Abschnitt 7.1.12.1: ToString, angewendet auf den Nummerntyp . Hier werden die Regeln für die Konvertierung einer Zahl m in einen String festgelegt. Der Schlüsselteil ist Punkt 5, in dem eine ganze Zahl s erzeugt wird, deren Ziffern in der String-Darstellung von m verwendet werden :

lassen n , k und s ganze Zahlen , so dass k ≥ 1, 10 k -1s <10 k , der Zahlenwert für s 10 × n - k ist m , und k ist so klein wie möglich. Es ist zu beachten, dass k die Anzahl der Stellen in der Dezimaldarstellung von s ist , dass s nicht durch 10 teilbar ist und dass die niedrigstwertige Ziffer von s durch diese Kriterien nicht unbedingt eindeutig bestimmt wird.

Der Schlüsselteil hier ist die Anforderung, dass " k so klein wie möglich ist". Was die Forderung beläuft sich auf eine Anforderung , dass eine Nummer m, den Wert String(m)muss eine möglichst geringe Anzahl von Stellen , während immer noch die Anforderung erfüllt , dass Number(String(m)) === m. Da wir das bereits wissen 0.015 === 0.0150000000000000001, ist jetzt klar, warum String(0.0150000000000000001) === '0.015'das wahr sein muss.

Natürlich hat keine dieser Diskussionen direkt beantwortet, was zurückkehren roundTo2DP(m) sollte . Wenn mder genaue Wert 0.01499999999999999944488848768742172978818416595458984375 ist, die Zeichenfolgendarstellung jedoch '0.015' lautet, was ist dann die richtige Antwort - mathematisch, praktisch, philosophisch oder was auch immer -, wenn wir sie auf zwei Dezimalstellen runden?

Darauf gibt es keine einzige richtige Antwort. Dies hängt von Ihrem Anwendungsfall ab. Sie möchten wahrscheinlich die Zeichenfolgendarstellung respektieren und nach oben runden, wenn:

  • Der dargestellte Wert ist von Natur aus diskret, z. B. ein Währungsbetrag in einer Währung mit 3 Dezimalstellen wie Dinar. In diesem Fall wird der wahre Wert einer Zahl wie 0,015 ist 0,015, und die 0,0149999999 ... Darstellung , dass es in binärer Gleitkomma- erhält , ist ein Rundungsfehler. (Natürlich werden viele vernünftigerweise argumentieren, dass Sie eine Dezimalbibliothek für die Behandlung solcher Werte verwenden und sie niemals als binäre Gleitkommazahlen darstellen sollten.)
  • Der Wert wurde von einem Benutzer eingegeben. Auch in diesem Fall ist die genaue eingegebene Dezimalzahl "wahr" als die nächste binäre Gleitkommadarstellung.

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

Diese beiden Ansätze erfordern unterschiedlichen Code. Um die String-Darstellung der Zahl zu respektieren, können wir (mit ziemlich viel subtilem Code) unsere eigene Rundung implementieren, die direkt auf die String-Darstellung wirkt, Ziffer für Ziffer, wobei wir denselben Algorithmus verwenden, den Sie in der Schule verwendet hätten, als Sie wurden gelehrt, wie man Zahlen rundet. Im Folgenden finden Sie ein Beispiel, das die Anforderung des OP berücksichtigt, die Zahl "nur bei Bedarf" auf 2 Dezimalstellen darzustellen, indem nach dem Dezimalpunkt nachgestellte Nullen entfernt werden. Möglicherweise müssen Sie 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+$/, '')
}

Anwendungsbeispiel:

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

Die obige Funktion ist wahrscheinlich die, die Sie verwenden möchten, um zu vermeiden, dass Benutzer jemals feststellen, dass eingegebene Zahlen falsch gerundet werden.

(Alternativ können Sie auch die round10- Bibliothek ausprobieren, die eine ähnlich verhaltende Funktion mit einer völlig anderen Implementierung bietet.)

Aber was ist, wenn Sie die zweite Art von Zahl haben - einen Wert aus einer kontinuierlichen Skala, bei der es keinen Grund zu der Annahme gibt, dass ungefähre Dezimaldarstellungen mit weniger Dezimalstellen genauer sind als solche mit mehr? In diesem Fall möchten wir die String-Darstellung nicht berücksichtigen, da diese Darstellung (wie in der Spezifikation erläutert) bereits gerundet ist. Wir wollen nicht den Fehler machen, zu sagen "0.014999999 ... 375 rundet auf 0.015, was auf 0.02 rundet, also 0.014999999 ... 375 rundet auf 0.02".

Hier können wir einfach die eingebaute toFixedMethode verwenden. Beachten Sie, dass wir durch Aufrufen Number()des von toFixedzurückgegebenen Strings eine Zahl erhalten, deren String-Darstellung keine nachgestellten Nullen enthält (dank der Art und Weise, wie JavaScript die String-Darstellung einer Zahl berechnet, die weiter oben in dieser Antwort erläutert 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);
}

Dies funktioniert in einigen Randfällen nicht: try ( jsfiddle ) with roundStringNumberWithoutTrailingZeroes(362.42499999999995, 2). Erwartetes Ergebnis (wie in PHP echo round(362.42499999999995, 2)) : 362.43. Tatsächliches Ergebnis:362.42
Dr. Gianluigi Zane Zanettini

1
@ Dr.GianluigiZaneZanettini Huh. Seltsam. Ich bin nicht sicher, warum roundPHPs 362,43 geben. Das scheint intuitiv falsch zu sein, da 362.42499999999995 weniger als 362.425 ist (in Mathematik und Code - 362.42499999999995 < 362.425gilt sowohl für JS als auch für PHP). Die Antwort von PHP minimiert auch nicht den Abstand zwischen der ursprünglichen und der gerundeten Gleitkommazahl, da 362.43 - 362.42499999999995 > 362.42499999999995 - 362.42. Nach php.net/manual/en/function.round.php , PHP roundfolgt dem C99 - Standard; Ich muss mich in das Land C wagen, um zu verstehen, was los ist.
Mark Amery

1
Nachdem ich mir die Implementierung der Rundung in der PHP-Quelle angesehen habe , habe ich keine verdammte Ahnung, was sie tut. Es ist eine schrecklich komplizierte Implementierung voller Makros und Verzweigungen sowie Zeichenfolgenkonvertierungen und Verzweigungen nach hartcodierten magischen Präzisionswerten. Ich bin mir nicht sicher, was ich darüber hinaus sagen soll. "Die Antwort von PHP ist offensichtlich falsch, und wir sollten einen Fehlerbericht einreichen." Wie haben Sie übrigens die Nummer 362.42499999999995 gefunden?
Mark Amery

1
@ Dr.GianluigiZaneZanettini Ich habe einen Fehlerbericht erstellt: bugs.php.net/bug.php?id=75644
Mark Amery

2
@ Dr.GianluigiZaneZanettini " Mache ich irgendeinen Sinn?" - Nein; So funktioniert Rundung nicht. 1.000005 endet mit fünf, aber wenn auf die nächste ganze Zahl gerundet, sollte die Antwort 1 und nicht 2 sein. Ebenso endet 1499995 mit fünf, aber wenn auf die nächste Million gerundet, sollte das Ergebnis 1000000 und nicht 2000000 sein. Im Fall von 362.42499999999995 Wenn auf 2 DP gerundet wird, sollte die Rundungsrichtung die dritte Dezimalstelle bestimmen , die 4 ist.
Mark Amery

76

Betrachten Sie .toFixed()und .toPrecision():

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


1
toFixed fügt jedem Wert die Dezimalstellen hinzu, egal was passiert.
Stinkycheeseman

13
Beide sind hier nutzlos
Esailija

Leider fügen beide Funktionen zusätzliche Dezimalstellen hinzu, die @stinkycheeseman anscheinend nicht möchte.
Jackwanders


2
Sie geben Zeichenfolgen und keine Zahlen zurück, also formatieren sie und berechnen nicht.
saimiris_devel

63

Eine präzise 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

Jemand hat dies auch auf GitHub und npm eingestellt: github.com/jhohlfeld/round10
Jo Liss

Math.round10(3544.5249, -2)Nein , gibt 3544,52 statt 3544,53 zurück
Matija

2
@ Matija Wolfram Alpha sagt auch 3544.52. Sie möchten den Fehler zwischen der aktuellen Zahl und der gerundeten Näherung minimieren. Die nächste Annäherung 3544.5249an 2 Dezimalstellen ist 3544.52(Fehler = 0,0049). Wenn dies 3544.53der Fall wäre, wäre der Fehler 0,0051. Sie führen eine sukzessive Rundung durch, dh Math.round10 (Math.round10 (3544.5249, -3), -2), was einen größeren Rundungsfehler ergibt und daher nicht wünschenswert ist.
Benutzer

3
@Matija, aus mathematischer Sicht, 3544.5249 gerundet ist 3544.52, nicht 3544.53, daher ist dieser Code korrekt. Wenn Sie möchten, dass es in diesem Fall und in solchen Fällen auf 3544,53 gerundet wird (auch hart ist falsch), tun Sie number += 0.00011
Folgendes

@ Matija: Ich denke, die Funktion funktioniert wie es sollte. Vielleicht möchten Sie die Rundung wie Math.round10( Math.round10(3544.5249, -3) , -2)
folgt wiederholen

60

Keine der hier gefundenen Antworten ist richtig . @stinkycheeseman gebeten, runden , Sie alle Zahl gerundet.

Verwenden Sie zum Aufrunden Folgendes:

Math.ceil(num * 100)/100;

15
Die Beispielein- und -ausgabe zeigt, dass die Frage zwar "Aufrunden ..." lautete, aber eigentlich "Aufrunden auf ..." sein sollte.
JayDM

2
@stinkycheeseman wies auf einen Fehler in einem bestimmten Fall hin, er wollte nicht immer wie Ceil aufrunden, er wollte nur 0,005 auf 0,01
aufrunden

9
Beim Testen wurde ein seltsamer Fehler gefunden Math.ceil(1.1 * 100)/100;- es kehrt zurück 1.11, da 1.1 * 100 110.00000000000001laut brandneuen modernen Browsern Firefox, Chrome, Safari und Opera ... IE, in alter Mode, immer noch denkt 1.1*100=1100.
Skobaljic

1
@ Skobaljic versuchenMath.ceil(num.toFixed(4) * 100) / 100
Baumgesicht

1
@treeface Math.ceil((1.1).toFixed(4) * 100) / 100wird auch 1.11in Firefox zurückkehren, das Problem / der Fehler moderner Browser liegt in der Multiplikation und die Leute sollten darüber Bescheid wissen (ich habe damals zum Beispiel an einem Lotteriespiel gearbeitet).
Skobaljic

47

Hier ist eine einfache Möglichkeit, dies zu tun:

Math.round(value * 100) / 100

Möglicherweise möchten Sie jedoch eine separate Funktion erstellen, um dies für Sie zu tun:

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

Dann würden Sie einfach den Wert übergeben.

Sie können es auf eine beliebige Anzahl von Dezimalstellen runden, indem Sie einen zweiten Parameter hinzufügen.

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

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

2
Diese Lösung ist falsch, siehe stackoverflow.com/questions/38322372/…. Wenn Sie 156893.145 eingeben und mit der obigen Funktion runden, erhalten Sie 156893.14 anstelle von 156893.15 !!!
saimiris_devel

2
@saimiris_devel, Sie haben es geschafft, eine Instanz zu finden, in der die numerische Darstellung von Zahlen in JavaScript fehlschlägt. Sie haben Recht, dass es falsch rundet. Aber - das liegt daran, dass Ihre Probennummer, wenn sie mit 100 multipliziert wird, bereits fehlerhaft ist (15689314.499999998). Eine voll funktionsfähige Antwort würde eine Bibliothek erfordern, die speziell entwickelt wurde, um die Inkonsistenzen im Umgang von JavaScript mit reellen Zahlen zu berücksichtigen. Andernfalls könnten Sie wahrscheinlich alle Antworten, die auf diese Frage gegeben wurden, ungültig machen.
JayDM

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

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

1
Dies führt nicht immer zu den gleichen Ergebnissen, die Sie erhalten würden, wenn Sie die String-Darstellung Ihrer Zahl nehmen und sie runden würden. Zum Beispiel +(0.015).toFixed(2) == 0.01.
Mark Amery

35

Für mich gab Math.round () keine richtige Antwort. Ich fand, dass Fixed (2) besser funktioniert. Nachfolgend finden Sie Beispiele für beides:

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

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


Es ist wichtig zu beachten, dass toFixed keine Rundung durchführt und dass Math.round nur auf die nächste ganze Zahl rundet. Um die Dezimalstellen beizubehalten, müssen wir daher die ursprüngliche Zahl mit der Anzahl der Zehnerpotenzen multiplizieren, deren Nullen Ihre gewünschte Anzahl von Dezimalstellen darstellen, und dann das Ergebnis durch dieselbe Zahl dividieren. In Ihrem Fall: Math.round (43000/80000 * 100 * 100) / 100. Zuletzt kann toFixed (2) angewendet werden, um sicherzustellen, dass das Ergebnis immer zwei Dezimalstellen enthält (bei Bedarf mit nachgestellten Nullen) - perfekt für die Rechtsausrichtung einer Reihe von Zahlen vertikal dargestellt :)
Turbo

7
Beachten Sie auch, dass .toFIxed () eine Zeichenfolge und keine Zahl ausgibt.
Carpiediem

2
Dies löst immer noch nicht die Rundung für 1.005. (1.005).toFixed(2)ergibt sich noch zu 1.00.
DPac

34

Verwenden Sie diese Funktion Number(x).toFixed(2);


8
Wickeln Sie alles Numbererneut ein, wenn Sie nicht möchten, dass es als Zeichenfolge zurückgegeben wird:Number(Number(x).toFixed(2));

5
Der NumberAnruf ist nicht notwendig, x.toFixed(2)funktioniert.
Bgusach

3
@bgusach Nummernaufruf erforderlich, da die Anweisung x.toFixed (2) eine Zeichenfolge und keine Nummer zurückgibt. Um wieder in Zahl umzuwandeln, müssen wir mit Zahl
Mohan Ram

2
Bei Verwendung dieser Methode wird (1).toFixed(2)zurückgegeben 1.00, 1in diesem Fall wird jedoch ein Fragesteller benötigt .
Eugene Mala

1
Das funktioniert nicht, 1.005.toFixed(2)gibt nach, "1"wann es sein sollte "1.01".
Adam Jagosz

33

2017
Verwenden Sie einfach nativen Code.toFixed()

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

Wenn Sie streng sein und bei Bedarf Ziffern hinzufügen müssen, kann dies verwendet werden replace

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

3
Die toFixed-Methode gibt eine Zeichenfolge zurück. Wenn Sie ein Zahlenergebnis wünschen, müssen Sie das Ergebnis von toFixed an parseFloat senden.
Zambonilli

@ Zambonilli Oder multiplizieren Sie einfach mit 1, wenn es nötig ist. aber weil feste Zahl die meisten Fälle für die Anzeige und nicht für die Berechnung Zeichenfolge ist das richtige Format
pery mimon

2
-1; Dies wurde nicht nur toFixeddurch mehrere Antworten Jahre vor Ihrer vorgeschlagen, sondern erfüllt auch nicht die Bedingung "nur wenn erforderlich" in der Frage. (1).toFixed(2)gibt an, "1.00"wo der Fragesteller es wünscht "1".
Mark Amery

OK habe es. Ich füge auch für diesen Fall eine Lösung hinzu
pery mimon

Wenn Sie lodash verwenden, ist es noch einfacher: _.round (number, decimalPlace) Mein letzter Kommentar wurde gelöscht, da es ein Problem gibt. Lodash _.round funktioniert jedoch. 1,005 mit einer Dezimalstelle von 2 wird in 1,01 konvertiert.
Devin Fields

32

Probieren Sie diese leichte Lösung aus:

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

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

Weiß jemand, ob es einen Unterschied zwischen diesem und return Number(x.toFixed(digits))?

1
@ JoeRocc ... sollte keinen Unterschied machen, soweit ich sehen kann, da .toFixed()sowieso nur Zahlen erlaubt sind.
Petermeissner

4
Diese Antwort hat das gleiche Problem wie mehrmals auf dieser Seite erwähnt. Versuchen round(1.005, 2)Sie, ein Ergebnis von 1statt zu sehen 1.01.
MilConDoin

scheint eher ein problem der rundung algo? - Es gibt mehr als man sich vorstellen kann: en.wikipedia.org/wiki/Rounding ... round(0.995, 2) => 0.99; round(1.006, 2) => 1.01;; round(1.005, 2) => 1
Petermeissner

31

Es gibt verschiedene Möglichkeiten, dies zu tun. Für Leute wie mich die Lodash-Variante

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

Verwendungszweck:

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

Wenn Ihr Projekt jQuery oder lodash verwendet, finden Sie die richtige roundMethode auch in den Bibliotheken.

Update 1

Ich habe die Variante entfernt n.toFixed(2), weil sie nicht korrekt ist. Vielen Dank an avalanche1


Die zweite Option gibt eine Zeichenfolge mit genau zwei Dezimalstellen zurück. Die Frage fragt nur bei Bedarf nach Dezimalstellen. Die erste Option ist in diesem Fall besser.
Marcos Lima

@MarcosLima Number.toFixed()gibt eine Zeichenfolge zurück, aber mit einem Pluszeichen davor konvertiert der JS-Interpreter die Zeichenfolge in eine Zahl. Dies ist eine Syntax Zucker.
stanleyxu2005

alert((+1234).toFixed(2))Zeigt in Firefox "1234.00" an.
Marcos Lima

Auf Firefox alert(+1234.toFixed(2))wirft SyntaxError: identifier starts immediately after numeric literal. Ich bleibe bei der 1. Option.
Marcos Lima

Dies funktioniert in einigen Randfällen nicht: try ( jsfiddle ) with 362.42499999999995. Erwartetes Ergebnis (wie in PHP echo round(362.42499999999995, 2)) : 362.43. Tatsächliches Ergebnis:362.42
Dr. Gianluigi Zane Zanettini

26

Wenn Sie die lodash-Bibliothek verwenden, können Sie die runde Methode von lodash wie folgt verwenden.

_.round(number, precision)

Z.B:

_.round(1.7777777, 2) = 1.78

@Peter Die Funktionen, die Lodash bietet, sind im Vergleich zu Standard-Javascript wirklich gut. Ich habe jedoch gehört, dass Lodash im Vergleich zu Standard-JS Leistungsprobleme aufweist. codeburst.io/…
Madura Pradeep

1
Ich akzeptiere Ihren Standpunkt, dass die Verwendung von lodash Leistungsnachteile aufweist. Ich denke, dass diese Themen vielen Abstraktionen gemeinsam sind. Aber schauen Sie sich nur an, wie viele Antworten es in diesem Thread gibt und wie die intuitiven Lösungen für Randfälle fehlschlagen. Wir haben dieses Muster mit jQuery gesehen und das Root-Problem wurde gelöst, als Browser einen gemeinsamen Standard übernahmen, der die meisten unserer Anwendungsfälle löste. Leistungsengpässe wurden dann in die Browser-Engines verschoben. Ich denke, dasselbe sollte mit lodash passieren. :)
Peter

26

Seit ES6 gibt es eine "richtige" Möglichkeit (ohne Statik zu überschreiben und Problemumgehungen zu erstellen), dies mithilfe von toPrecision zu tun

var x = 1.49999999999;
console.log(x.toPrecision(4));
console.log(x.toPrecision(3));
console.log(x.toPrecision(2));

var y = Math.PI;
console.log(y.toPrecision(6));
console.log(y.toPrecision(5));
console.log(y.toPrecision(4));

var z = 222.987654
console.log(z.toPrecision(6));
console.log(z.toPrecision(5));
console.log(z.toPrecision(4));

dann können Sie einfach parseFloatund Nullen werden "verschwinden".

console.log(parseFloat((1.4999).toPrecision(3)));
console.log(parseFloat((1.005).toPrecision(3)));
console.log(parseFloat((1.0051).toPrecision(3)));

Das '1.005-Rundungsproblem' wird dadurch jedoch nicht gelöst, da es wesentlich dazu beiträgt, wie Float-Brüche verarbeitet werden .

console.log(1.005 - 0.005);

Wenn Sie für Bibliotheken geöffnet sind, können Sie bignumber.js verwenden

console.log(1.005 - 0.005);
console.log(new BigNumber(1.005).minus(0.005));

console.log(new BigNumber(1.005).round(4));
console.log(new BigNumber(1.005).round(3));
console.log(new BigNumber(1.005).round(2));
console.log(new BigNumber(1.005).round(1));
<script src="https://cdnjs.cloudflare.com/ajax/libs/bignumber.js/2.3.0/bignumber.min.js"></script>


3
(1.005).toPrecision(3)kehrt immer noch 1.00statt 1.01tatsächlich zurück.
Giacomo

toPrecisionGibt eine Zeichenfolge zurück, die den gewünschten Ausgabetyp ändert.
Adamduren

@Giacomo Es ist kein .toPrecisionMethodenfehler, es ist eine Spezifität von Gleitkommazahlen (welche Zahlen in JS sind) - versuchen Sie 1.005 - 0.005, es wird zurückgegeben 0.9999999999999999.
Shau-Kote

1
(1).toPrecision(3)gibt '1.00' zurück, aber der Fragesteller wollte 1in diesem Fall haben.
Eugene Mala

1
Wie @Giacomo sagte, scheint diese Antwort "signifikante Stellen" mit "Rundung auf eine Anzahl von Dezimalstellen" zu verwechseln. toPrecisiontut das Format, nicht das letztere, und ist keine Antwort auf die Frage des OP, obwohl es auf den ersten Blick relevant erscheinen mag, wird es viel falsch. Siehe en.wikipedia.org/wiki/Significant_figures . Zum Beispiel Number(123.4).toPrecision(2)kehrt zurück "1.2e+2"und Number(12.345).toPrecision(2)kehrt zurück "12". Ich würde auch @ adamdurens Argument zustimmen, dass es eine Zeichenfolge zurückgibt, die nicht wünschenswert ist (kein großes Problem, aber nicht wünschenswert).
Neek

23

MarkG und Lavamantis boten eine viel bessere Lösung als die akzeptierte. Es ist eine Schande, dass sie nicht mehr positive Stimmen bekommen!

Hier ist die Funktion, mit der ich die Gleitkomma-Dezimalstellen-Probleme löse, die auch auf MDN basieren . Es ist noch allgemeiner (aber weniger prägnant) als die Lösung von 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));
}

Verwenden Sie es mit:

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

Im Vergleich zu Lavamantis 'Lösung können wir ...

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

2
Ihre Lösung deckt einige Fälle nicht ab, im Gegensatz zur MDN-Lösung. Während es kürzer sein kann, ist es nicht genau ...
astorije

1
Runde (-1835,665,2) => -1835,66
Jorge Sampayo


18

Der einfachste Ansatz wäre, toFixed zu verwenden und dann nachfolgende Nullen mit der Number-Funktion zu entfernen:

const number = 15.5;
Number(number.toFixed(2)); // 15.5
const number = 1.7777777;
Number(number.toFixed(2)); // 1.78

Dies funktioniert nicht in allen Fällen. Führen Sie umfangreiche Tests durch, bevor Sie Antworten veröffentlichen.
Baburao

@baburao Bitte posten Sie einen Fall, in dem die oben genannte Lösung nicht funktioniert
Marcin Wanago

const number = 15; Number (number.toFixed (2)); //15.00 statt 15
Kevin Jhangiani

1
@ KevinJhangiani const number = 15; Number (number.toFixed (2)); // 15 - Ich habe es sowohl auf dem neuesten Chrome als auch auf Firefox getestet
Marcin Wanago

@ KevinJhangiani wie kommst du 15.00? Zahlen in JS speichern keine Dezimalstellen und jede Anzeige schneidet automatisch überschüssige Dezimalstellen ab (alle Nullen am Ende).
VLAZ

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

toFixed(2) hier ist 2 die Anzahl der Stellen, bis zu denen wir diese Zahl runden möchten.


Diese .toFixed () ist einfacher zu implementieren. geh es einfach einmal durch.
Ritesh Dhuri


14

Einfachster Weg:

+num.toFixed(2)

Es konvertiert es in einen String und dann zurück in eine Ganzzahl / Float.


Vielen Dank für diese einfachste Antwort. Was ist jedoch '+' in + num? Bei mir hat es nicht funktioniert, wenn der Dezimalwert in einer Zeichenfolge angegeben wurde. Ich habe: (num * 1) .toFixed (2).
Ethan

@momo ändere einfach das Argument toFixed()auf 3. So wäre es +num.toFixed(3). Das funktioniert so, wie es soll, 1.005 wird auf 1.00 gerundet, was 1 entspricht
bigpotato

1
@ Edmund Es soll 1,01 zurückgeben, nicht 1,00
mmm

13

Hier ist eine Prototypmethode:

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

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

13

Verwenden Sie so etwas wie "parseFloat (parseFloat (value) .toFixed (2))"

parseFloat(parseFloat("1.7777777").toFixed(2))-->1.78 
parseFloat(parseFloat("10").toFixed(2))-->10 
parseFloat(parseFloat("9.1").toFixed(2))-->9.1

1
nicht, wenn die Ungenauigkeit der Float-Darstellung eigen ist. Sie würden es einfach entfernen und dann denselben Fehler wieder einführen, indem Sie wieder in float konvertieren!
Ben McIntyre

12

Eine Möglichkeit, eine solche Rundung nur bei Bedarf zu erreichen , ist die Verwendung von Number.prototype.toLocaleString () :

myNumber.toLocaleString('en', {maximumFractionDigits:2, useGrouping:false})

Dies liefert genau die Ausgabe, die Sie erwarten, jedoch als Zeichenfolgen. Sie können diese weiterhin in Zahlen konvertieren, wenn dies nicht der erwartete Datentyp ist.


Dies ist bei weitem die sauberste Lösung, die es gibt, und umgeht alle komplizierten Gleitkommaprobleme, aber die MDN- Unterstützung ist immer noch unvollständig - Safari unterstützt noch keine Übergabe von Argumenten an toLocaleString.
Mark Amery

@MarkAmery Derzeit hat nur der Android-Browser einige Probleme: caniuse.com/#search=toLocaleString
ptyskju

12

Nach Durchlaufen verschiedener Iterationen aller möglichen Möglichkeiten, um eine wirklich genaue Dezimalrundungsgenauigkeit zu erzielen, ist es klar, dass die genaueste und effizienteste Lösung die Verwendung von Number.EPSILON ist. Dies bietet eine echte mathematische Lösung für das Problem der mathematischen Genauigkeit von Gleitkommazahlen. Es kann wie hier gezeigt einfach polyfilliert werden: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/EPSILON , um alle letzten verbleibenden IE-Benutzer zu unterstützen (dann vielleicht auch wir sollte damit aufhören).

Angepasst an die hier bereitgestellte Lösung: https://stackoverflow.com/a/48850944/6910392

Eine einfache Drop-In-Lösung, die eine genaue Dezimalrundung, Bodenbelag und Decke mit einer optionalen Präzisionsvariablen bietet, ohne eine ganze Bibliothek hinzuzufügen.

UPDATE: Wie Sergey in den Kommentaren feststellte, gibt es eine Einschränkung für diese (oder jede andere) Methode, auf die hingewiesen werden sollte. Bei Zahlen wie 0.0149999999999999999 treten immer noch Ungenauigkeiten auf, die darauf zurückzuführen sind, dass die absolute Grenze der Genauigkeitsbeschränkungen für die Speicherung von Gleitkommawerten erreicht wurde. Es gibt keine mathematische oder andere Lösung, die angewendet werden kann, um dies zu berücksichtigen, da der Wert selbst sofort als 0,015 ausgewertet wird. Sie können dies bestätigen, indem Sie diesen Wert einfach selbst in der Konsole aufrufen. Aufgrund dieser Einschränkung wäre es nicht einmal möglich, die Zeichenfolgenmanipulation zu verwenden, um diesen Wert zu reduzieren, da die Zeichenfolgendarstellung einfach "0,015" ist. Jede Lösung, um dies zu berücksichtigen, müsste logisch an der Datenquelle angewendet werden, bevor der Wert jemals in ein Skript übernommen wird.

var DecimalPrecision = (function(){
        if (Number.EPSILON === undefined) {
            Number.EPSILON = Math.pow(2, -52);
        }
        this.round = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.round((n + r) * o) / o;
        }
        this.ceil = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.ceil((n + r) * o) / o;
        }
        this.floor = function(n, p=2){
            let r = 0.5 * Number.EPSILON * n;
            let o = 1; while(p-- > 0) o *= 10;
            if(n < 0)
                o *= -1;
            return Math.floor((n + r) * o) / o;
        }
        return this;
    })();
    console.log(DecimalPrecision.round(1.005));
    console.log(DecimalPrecision.ceil(1.005));
    console.log(DecimalPrecision.floor(1.005));
    console.log(DecimalPrecision.round(1.0049999));
    console.log(DecimalPrecision.ceil(1.0049999));
    console.log(DecimalPrecision.floor(1.0049999));
    console.log(DecimalPrecision.round(2.175495134384,7));
    console.log(DecimalPrecision.round(2.1753543549,8));
    console.log(DecimalPrecision.round(2.1755465135353,4));


1
(DecimalPrecision.round (0.014999999999999999, 2)) // gibt 0.02 zurück
Sergey

Guter Fang! Das Problem ist bei der Gleitkommaspeicherung in JS, es wird immer einige Randfälle geben. Die gute Nachricht ist, dass die Mathematik, die Sie zuerst auf Number.EPSILON anwenden, feiner abgestimmt werden kann, um diese Kantenfälle weiter nach außen zu verschieben. Wenn Sie keine Möglichkeit für Randfälle garantieren möchten, ist Ihre einzige wirkliche Lösung die Manipulation von Zeichenfolgen und dann die Mathematik. In dem Moment, in dem Sie eine mathematische Berechnung für den Wert durchführen (selbst wenn Sie versuchen, die Dezimalstelle zu verschieben), haben Sie den Fehler bereits erzeugt.
KFish

Tatsächlich liegt dies bei weiterer Prüfung nicht an der Mathematik, sondern das Problem tritt sofort beim Aufrufen des angegebenen Werts auf. Sie können dies einfach bestätigen, indem Sie diese Nummer in die Konsole eingeben und sehen, dass sie sofort 0,015 ergibt. Daher würde dies die absolute Genauigkeitskante für jede Gleitkommazahl in JS darstellen. In diesem Fall konnten Sie nicht einmal in einen String konvertieren und manipulieren, da der String-Wert "
0.015

11

Dies ist die einfachste und eleganteste Lösung (und ich bin der Beste der Welt;):

function roundToX(num, X) {    
    return +(Math.round(num + "e+"+X)  + "e-"+X);
}
//roundToX(66.66666666,2) => 66.67
//roundToX(10,2) => 10
//roundToX(10.904,2) => 10.9

4
Das ist eine gute Möglichkeit, die akzeptierte Antwort neu zu schreiben, um ein Argument in ENotation zu akzeptieren .
AxelH

1
Dies funktioniert in einigen Randfällen nicht: try ( jsfiddle ) roundToX(362.42499999999995, 2). Erwartetes Ergebnis (wie in PHP echo round(362.42499999999995, 2)) : 362.43. Tatsächliches Ergebnis:362.42
Dr. Gianluigi Zane Zanettini

6
IMHO, Ihr PHP-Ergebnis ist falsch. Unabhängig davon, was nach der dritten Dezimalstelle steht, sollte die zweite Dezimalstelle gleich 5 sein, sollte die zweite Dezimalstelle gleich bleiben. Das ist die mathematische Definition.
Soldeplata Saketos

1
Um noch prägnanter zu sein, kann "e +" stattdessen einfach "e" sein.
Lonnie Best
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.