Wie kann ich eine Zahl in JavaScript runden? .toFixed () gibt einen String zurück?

176

Vermisse ich hier etwas?

var someNumber = 123.456;
someNumber = someNumber.toFixed(2);
alert(typeof(someNumber));
//alerts string

Warum wird.toFixed()eine Zeichenfolge zurückgegeben?

Ich möchte die Zahl auf 2 Dezimalstellen runden.

Derek Adair
quelle
7
Weil es entworfen ist, um einen String zurückzugeben?
Kennytm
2
Für mich scheint es nur seltsam. .toFixed () arbeitet nur mit Zahlen ... richtig?
Derek Adair
10
Ich verstehe, dass Math.round () wie erwartet funktioniert. Ich habe nur gefragt, warum eine Funktion, die mit Zahlen arbeitet, eine Zeichenfolge zurückgibt ...
Derek Adair
3
Menschen, die im Jahr 2017 leben, sollten Bibliotheken wie lodash.com/docs/4.17.4#ceil
Yves M.
1
_ Auch. Anzahl? noch nicht auf seinen Bruder aktualisiert.
Jenna Leaf

Antworten:

124

Es gibt eine Zeichenfolge zurück, da 0.1 und Potenzen davon (die zum Anzeigen von Dezimalbrüchen verwendet werden) in binären Gleitkommasystemen nicht darstellbar sind (zumindest nicht mit voller Genauigkeit).

Zum Beispiel ist 0,1 wirklich 0,10000000000000000555511151231257827021181583404541015625 und 0,01 ist wirklich 0,01000000000000000020816681711721685132943093776702880859375. (Danke, BigDecimaldass du meinen Standpunkt bewiesen hast .:-P)

Daher ist die Ausgabe als Zeichenfolge (ohne Dezimal-Gleitkomma- oder rationalen Zahlentyp) die einzige Möglichkeit, die Genauigkeit genau auf die für die Anzeige erforderliche Genauigkeit zu bringen.

Chris Jester-Young
quelle
28
Zumindest Javascript könnte mir Fingerarbeit ersparen und es wieder in eine Zahl umwandeln ... meine Güte ...
Derek Adair
10
@Derek: Ja, aber sobald Sie es wieder in eine Zahl umwandeln, stoßen Sie wieder auf dieselben Ungenauigkeitsprobleme. :-P JS hat keine dezimalen Gleitkomma- oder rationalen Zahlen.
Chris Jester-Young
1
@DerekAdair Ich habe kürzlich einen Beitrag geschrieben, der dies noch weiter erklärt, an dem Sie vielleicht interessiert sind. Viel Spaß! stackoverflow.com/a/27030789/13
Chris Jester-Young
7
Eigentlich hat mich das dazu veranlasst, mich ziemlich intensiv mit diesem Thema zu beschäftigen! Danke für deine Hilfe!
Derek Adair
2
Ihre Antwort ist leicht irreführend: Es toFixedhandelt sich um eine Formatierungsfunktion, die ausschließlich dazu dient, eine Zahl in eine Zeichenfolge umzuwandeln und sie mit der angegebenen Anzahl von Dezimalstellen zu formatieren. Der Grund, warum eine Zeichenfolge zurückgegeben wird, liegt darin, dass eine Zeichenfolge zurückgegeben werden soll. Wenn sie toStringFixedstattdessen benannt würde, wäre OP über die Ergebnisse nicht überrascht. Das einzige Problem hierbei ist, dass OP erwartet hat, dass es so funktioniert Math.round, ohne die JS-Referenz zu konsultieren.
Groo
176

Number.prototype.toFixedist eine Funktion zum Formatieren einer Zahl vor dem Ausdrucken. Es ist aus der Familie der toString, toExponentialund toPrecision.

Um eine Zahl zu runden, gehen Sie folgendermaßen vor:

someNumber = 42.008;
someNumber = Math.round( someNumber * 1e2 ) / 1e2;
someNumber === 42.01;

// if you need 3 digits, replace 1e2 with 1e3 etc.
// or just copypaste this function to your code:

function toFixedNumber(num, digits, base){
  var pow = Math.pow(base||10, digits);
  return Math.round(num*pow) / pow;
}

.

Oder wenn Sie eine " native-like " -Funktion wünschen , können Sie den Prototyp erweitern:

Number.prototype.toFixedNumber = function(digits, base){
  var pow = Math.pow(base||10, digits);
  return Math.round(this*pow) / pow;
}
someNumber = 42.008;
someNumber = someNumber.toFixedNumber(2);
someNumber === 42.01;


//or even hexadecimal

someNumber = 0xAF309/256  //which is af3.09
someNumber = someNumber.toFixedNumber(1, 16);
someNumber.toString(16) === "af3.1";

Beachten Sie jedoch , dass die Verschmutzung des Prototyps beim Schreiben eines Moduls als schlecht angesehen wird, da Module keine Nebenwirkungen haben sollten. Also, für ein Modul verwenden , um die erste Funktion .

m93a
quelle
12
Ich denke, das ist die beste Antwort. Es vermeidet die Typkonvertierung. Großartige Soße!
Phil
1
Gute Antwort! Allerdings ... Ich mache seit ungefähr 20 Jahren JavaScript, aber ich kann nicht herausfinden, warum Sie diese + (...) Konstruktion um den Rückgabewert verwenden? Danke @sam für das Einreiben :) Da ich nie zu alt bin, um es zu lernen, bitte erläutern Sie :-)
HammerNL
1
@HammerNL Trotz Sams Überzeugung macht es akut nichts :) Es ist nur eine Übung - IDEs erkennen diese Funktion als type Number. Die Sache ist, dass +(anyValue)immer eine Zahl zurückgegeben wird - z. +("45")kehrt zurück 45, +(new Number(42))kehrt zurück 42. Es ist ein bisschen so, als würde man die Funktion stark tippen. Wenn Sie es sich zur Gewohnheit machen, können Sie viele Fehler vermeiden :)
m93a
Warum ist dies nicht in Kern-Javascript gebacken: s
Webmaster
2
Die Antwort von someNumber = Math.round( 42.008 * 1e2 ) / 1e2;ist nicht 42.01, es ist ~42.0099999999999980. Grund: Die Nummer 42.01existiert nicht und wird auf die nächste vorhandene Nummer gerundet. Übrigens, prüfen Sie die Nummern, indem Sie toPrecision(18)sie mit allen relevanten Ziffern ausdrucken.
Wiimm
118

Ich habe dieses Problem gelöst, indem ich Folgendes geändert habe:

someNumber = someNumber.toFixed(2)

... dazu:

someNumber = +someNumber.toFixed(2);

Dadurch wird die Zahl jedoch in eine Zeichenfolge konvertiert und erneut analysiert, was sich erheblich auf die Leistung auswirkt. Wenn Sie Wert auf Leistung oder Typensicherheit legen, überprüfen Sie auch die anderen Antworten.

Eva Juan
quelle
39
Nein nein Nein Nein Nein! Tu das nicht ! Die Konvertierung von Zahlen in Zeichenfolgen, nur um sie zu runden, ist eine sehr schlechte Praxis ! Stattdessen tun someNumber = Math.round(someNumber * 1e2) / 1e2! Siehe meine Antwort für einen allgemeineren Weg.
M93a
@ m93a - warum ist es so eine schlechte Praxis?
JCZAPLEW
3
@jczaplew Denn wenn man es auf diese Weise tun, wird die 32 - Bit - Binärzahl in einen String umgewandelt, 16bits für jeden gott verdammt dezimal mit digit ! (Übrigens ist das Speichern von Zahlen in UTF-16 nicht das bequemste, was Sie tun würden.) Und dann wird die Zeichenfolge wieder in eine 32-Bit-Gleitkommazahl konvertiert. Ziffer für Ziffer. (Wenn ich alle Tests ignoriere, die zuvor durchgeführt werden müssen, um den richtigen Parsing-Algorithmus auszuwählen.) Und das alles umsonst, wenn man bedenkt, dass man es mit 3 schnellen Operationen auf dem Float machen kann.
m93a
2
@ m93a also aus leistungsgründen? Hat dies tatsächlich einen spürbaren Einfluss auf die Leistung?
Sebastianb
2
@jczaplew, weil Strings (1) praktisch langsam und (2) theoretisch falsch sind.
Pacerier
28

Warum nicht verwenden parseFloat?

var someNumber = 123.456;
someNumber = parseFloat(someNumber.toFixed(2));
alert(typeof(someNumber));
//alerts number
Sirlunchalot
quelle
15

Ich habe es gelöst, indem ich es mit der JavaScript- Number()Funktion wieder in eine Zahl konvertiert habe

var x = 2.2873424;
x = Number(x.toFixed(2));
Nizar
quelle
12

Natürlich gibt es einen String zurück. Wenn Sie die numerische Variable runden möchten, verwenden Sie stattdessen Math.round (). Der Punkt von toFixed besteht darin, die Zahl mit einer festen Anzahl von Dezimalstellen zu formatieren, die dem Benutzer angezeigt werden sollen .

Joel Coehoorn
quelle
4

Sie können einfach ein '+' verwenden, um das Ergebnis in eine Zahl umzuwandeln.

var x = 22.032423;
x = +x.toFixed(2); // x = 22.03
Meisam
quelle
3

Was würden Sie erwarten, wenn es eine Zahl formatieren soll? Wenn Sie eine Nummer haben, können Sie so ziemlich nichts damit anfangen, weil zB 2 == 2.0 == 2.00usw., also muss es eine Zeichenfolge sein.

Tomas Vana
quelle
3

Um ein Beispiel dafür zu liefern, warum es sich um eine Zeichenfolge handeln muss:

Wenn Sie 1.toFixed (2) formatieren, erhalten Sie '1.00'.

Dies ist nicht dasselbe wie 1, da 1 keine 2 Dezimalstellen hat.


Ich weiß, dass JavaScript nicht gerade eine Leistungssprache ist, aber es besteht die Möglichkeit, dass Sie eine bessere Leistung für eine Rundung erzielen, wenn Sie Folgendes verwenden: roundValue = Math.round (Wert * 100) * 0,01

Pyro
quelle
2

Weil es hauptsächlich darum geht, Zahlen anzuzeigen? Wenn Sie Zahlen runden möchten, verwenden Sie diese Math.round()mit geeigneten Faktoren.

Christoph
quelle
aber es zeigt NUMMERN an, sollte es also keine "Zahl" zurückgeben?
Derek Adair
3
@Derek: Nur so wie das '42'eine Zahl ist ... was es nicht ist. Nur weil eine Zeichenfolge nur Ziffern enthält, wird sie nicht zu einer Zahl. Dies ist kein PHP. :-P
Chris Jester-Young
lol. Es ist keine Zeichenfolge, die eine Zahl enthält ... Es ist eine Zahl, die an eine Methode übergeben wird. Die Methode nimmt eine Zahl und gibt eine Zeichenfolge zurück.
Derek Adair
@DerekAdair richtig, aber ein Browser kann keine Zahl anzeigen, er zeigt Zeichenfolgen an, also die Konvertierung.
Nick M
1

Hier ist eine etwas funktionalere Version der Antwort m93a.

const toFixedNumber = (toFixTo = 2, base = 10) => num => {
  const pow = Math.pow(base, toFixTo)
  return +(Math.round(num * pow) / pow)
}

const oneNumber = 10.12323223

const result1 = toFixedNumber(2)(oneNumber) // 10.12
const result2 = toFixedNumber(3)(oneNumber) // 10.123

// or using pipeline-operator
const result3 = oneNumber |> toFixedNumber(2) // 10.12
Sartaj
quelle
Es ist eine praktische Funktion. Für undefinierte Typen funktioniert es nicht. Ich habe Code für diesen Fall hinzugefügt. if (num! == undefined) {return + (Math.round (num * pow) / pow)} else {return 0; }
Dino Liu