Abschnitt 8 · Lektion 4
Zahlen formatieren
Eine Zahl im Speicher und eine Zahl auf dem Bildschirm sind zwei verschiedene Dinge. 1234.5 ist die eine, 1.234,50 € die andere, und dazwischen liegt diese Lektion. Datum und Uhrzeit bleiben Abschnitt 19 vorbehalten, weil sie dort mit Date zusammengehören.
Nachkommastellen festzurren
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> const wert = 2 / 3;
console.log("Roh:", wert);
// Auf zwei Nachkommastellen, gerundet.
console.log("Zwei Stellen:", wert.toFixed(2));
// Es füllt auch auf, wenn die Stellen fehlen.
console.log("Aufgefüllt:", (5).toFixed(2));
// Und jetzt der Punkt: das Ergebnis ist ein Text.
console.log("Typ:", typeof wert.toFixed(2));
// Damit rechnet man nicht mehr, sondern verkettet.
console.log("Plus 1:", wert.toFixed(2) + 1);
// Zurück in eine Zahl geht mit Number, siehe Lektion 2.7.
console.log("Wieder Zahl:", Number(wert.toFixed(2)) + 1);
// Der Punkt bleibt dabei ein Punkt. Ein deutsches Komma
// macht toFixed nicht daraus. toFixed schneidet eine Zahl auf eine feste Zahl von Nachkommastellen und rundet dabei. Es füllt auch auf: aus 5 wird 5.00, wenn du zwei Stellen verlangst. Für Preise ist genau das gewollt.
Und dann der Haken: toFixed liefert einen Text. Keine Zahl. Das steht in keiner Fehlermeldung, es fällt erst auf, wenn du damit weiterrechnest. wert.toFixed(2) + 1 verkettet dann, statt zu addieren, genau wie in Lektion 2.7.
Sichtbar wird der Unterschied in der Eingabezeile der Console: (5).toFixed(2) antwortet mit "5.00" in Anführungszeichen, und die sind die Auskunft. In deiner eigenen Ausgabe steht dagegen 5.00 ohne alles, und daran ist nicht zu erkennen, was du in der Hand hältst.
Zurück kommst du mit Number, aber die bessere Frage ist, ob du überhaupt hin musstest. Formatieren ist der letzte Schritt. Rechne mit Zahlen, formatiere erst zur Ausgabe, dann stellt sich das Problem gar nicht.
Der Punkt bleibt dabei übrigens ein Punkt. toFixed kennt keine deutsche Schreibweise, dafür ist der dritte Abschnitt da.
Runden, und was bei minus passiert
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> // Bei positiven Zahlen ist alles wie erwartet.
console.log("round 2.5:", Math.round(2.5));
console.log("floor 2.5:", Math.floor(2.5));
console.log("ceil 2.5:", Math.ceil(2.5));
// Bei negativen Zahlen wird es interessant.
console.log("round -2.5:", Math.round(-2.5));
console.log("floor -2.5:", Math.floor(-2.5));
console.log("ceil -2.5:", Math.ceil(-2.5));
// round rundet immer zur größeren Zahl hin, nicht vom
// Nullpunkt weg. Deshalb wird aus -2.5 die -2 und nicht die -3.
console.log("round -2.4:", Math.round(-2.4));
console.log("round -2.6:", Math.round(-2.6));
// Wer wirklich nur die Nachkommastellen abschneiden will,
// nimmt trunc. Das tut bei beiden Vorzeichen dasselbe.
console.log("trunc -2.9:", Math.trunc(-2.9));
console.log("trunc 2.9:", Math.trunc(2.9)); Math.round rundet auf die nächste ganze Zahl, Math.floor immer ab, Math.ceil immer auf. Bei positiven Zahlen ist das unauffällig.
Bei negativen Zahlen überrascht Math.round. Aus -2.5 wird -2 und nicht -3. Der Grund ist die Regel dahinter: bei genau einem halben rundet JavaScript zur größeren Zahl hin, nicht vom Nullpunkt weg. Bei 2.5 fällt das nicht auf, weil beides dasselbe Ergebnis hat.
Auch floor und ceil heißen bei negativen Zahlen leicht anders, als man denkt: floor geht Richtung minus unendlich, aus -2.5 wird also -3. Wer wirklich nur die Nachkommastellen abschneiden will, nimmt Math.trunc, und das tut bei beiden Vorzeichen dasselbe.
Denk daran, dass Geld nach Lektion 2.3 ohnehin in Cent gerechnet wird. Dann kommt das Runden nur an einer Stelle vor, und diese Sonderfälle treffen dich seltener.
Der richtige Weg
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> const zahl = 1234567.891;
// Deutsche Schreibweise: Punkt als Tausendertrenner,
// Komma als Dezimaltrenner.
console.log("Deutsch:", new Intl.NumberFormat("de-DE").format(zahl));
// Zum Vergleich die englische, genau andersherum.
console.log("Englisch:", new Intl.NumberFormat("en-US").format(zahl));
// Feste Nachkommastellen, hier immer zwei.
const zweiStellen = new Intl.NumberFormat("de-DE", {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
console.log("Zwei Stellen:", zweiStellen.format(1234.5));
// Währung: Zeichen und Stellung kommen aus der Sprache.
const euro = new Intl.NumberFormat("de-DE", {
style: "currency",
currency: "EUR",
});
console.log("Euro:", euro.format(1234.5));
console.log("Null Euro:", euro.format(0));
// Und Prozent, das rechnet gleich mal hundert.
const prozent = new Intl.NumberFormat("de-DE", { style: "percent" });
console.log("Prozent:", prozent.format(0.25)); Intl.NumberFormat ist der Teil des Browsers, der weiß, wie Zahlen in verschiedenen Sprachen geschrieben werden. Du gibst eine Sprachkennung an, hier de-DE, und bekommst einen Formatierer, den du beliebig oft benutzen kannst.
Aus dieser einen Angabe folgt alles Weitere: Punkt als Tausendertrenner, Komma als Dezimaltrenner, das Euro-Zeichen hinter der Zahl. Im Englischen ist es genau andersherum, und du musst dafür nichts umbauen.
Über das zweite Argument stellst du ein, was formatiert wird. minimumFractionDigits und maximumFractionDigits legen die Nachkommastellen fest, style: "currency" mit currency: "EUR" macht eine Währung daraus, style: "percent" rechnet gleich mal hundert.
Leg den Formatierer einmal an und benutze ihn wieder. Ihn in einer Schleife jedes Mal neu zu bauen ist die Sorte Verschwendung, die man leicht vermeidet.
Und warum nicht von Hand
<h1>Ausgabe</h1>
<p>Was dieses Beispiel tut, steht in der Console.</p> const euro = new Intl.NumberFormat("de-DE", {
style: "currency",
currency: "EUR",
});
// Der naive Weg mit replace sieht zuerst richtig aus.
function vonHand(zahl) {
return zahl.toFixed(2).replace(".", ",") + " Euro";
}
console.log("Von Hand:", vonHand(19.9));
console.log("Fertig:", euro.format(19.9));
// Bei größeren Zahlen fehlt der Tausenderpunkt.
console.log("Von Hand:", vonHand(1234567.5));
console.log("Fertig:", euro.format(1234567.5));
// Das Leerzeichen vor dem Währungszeichen ist übrigens keines.
// Es ist ein geschütztes, damit die Zeile dort nicht umbricht.
const fertig = euro.format(19.9);
console.log("Als Zeichen:", fertig.split("").join("|")); Der naive Weg ist verlockend: toFixed(2), dann den Punkt gegen ein Komma tauschen, dann " Euro" anhängen. Drei Handgriffe, sieht richtig aus, und bei 19,90 Euro stimmt es sogar.
Bei größeren Zahlen fehlt der Tausenderpunkt. Den müsstest du selbst einbauen, von rechts nach links in Dreiergruppen, und spätestens dort wird aus der Zeile eine Funktion mit einer Schleife. Danach kommen negative Zahlen, dann andere Währungen, dann Sprachen, in denen das Zeichen vorn steht.
Der Punkt ist nicht, dass es unmöglich wäre. Der Punkt ist, dass jemand das schon gemacht hat, dass es im Browser eingebaut ist und dass die Version dort auch die Fälle kennt, an die du nicht denkst.
Einer davon steht am Ende des Beispiels: das Zeichen zwischen Zahl und Euro-Symbol ist kein gewöhnliches Leerzeichen, sondern ein geschütztes. Es sorgt dafür, dass die Zeile dort nicht umbricht. Man sieht ihm das nicht an, und wer den formatierten Text später auf ein normales Leerzeichen prüft, sucht lange.
Zum Mitnehmen
toFixed liefert einen Text und keine Zahl. Wer damit weiterrechnet, bekommt entweder eine Verkettung oder NaN, und beides fällt erst später auf.
Jetzt du
Basis Konto, kostenlosZu dieser Lektion gehört eine Aufgabe. Du schreibst den Code selbst, und nach jedem Lauf sagt dir eine Prüfliste, was schon stimmt.
Dafür brauchst du das Basis Konto. Es kostet nichts, und ein Passwort gibt es auch nicht.
Was in dieser Lektion steckt
-
Artikel mit 4 Beispielen zum Ausprobieren
Steht hier, ohne Konto lesbar.
-
Aufgabe im Editor, direkt im Browser geprüft
Öffnet sich mit dem Basis Konto.