Zahlen formatieren in JavaScript: toFixed und Intl.NumberFormat

Auf zwei Nachkommastellen runden, 1 234 567,89 im französischen Format anzeigen, eine Währung, einen Prozentsatz oder „1,4 k“: die nativen Methoden, um Zahlen in JavaScript zu formatieren, ihre Fallen und was von Numeral.js bleibt.

Zahlen in JavaScript formatieren und umrechnen mit Numeral
Schnelle Antwort

Zum Anzeigen einer Zahl nimmst du Intl.NumberFormat oder die Kurzform toLocaleString(): (1234.5).toLocaleString('fr-FR', { minimumFractionDigits: 2 }) ergibt „1 234,50“, und die Option style: 'currency' ergänzt die Währung. Zum Runden ohne Anzeige nimmst du Math.round(n * 100) / 100; toFixed(2) rundet ebenfalls, gibt aber einen String zurück.

Eine Zahl wird als Number berechnet und als String angezeigt: „1 234,50 €“, „25,6 %“, „1,4 k Aufrufe“. Dazwischen musst du runden, ein Tausendertrennzeichen setzen, Komma oder Punkt wählen. JavaScript erledigt das alles ohne Bibliothek, mit toFixed, toLocaleString und vor allem Intl.NumberFormat. Alle Ausgaben unten wurden unter Node.js 22 ausgeführt.

Eine Zahl runden

Math.round, Math.floor, Math.ceil, Math.trunc

javascript
Math.round(2.5);   // 3   : zum nächsten Wert, .5 geht nach oben
Math.round(-2.5);  // -2  : Richtung +unendlich, nicht „weg von null“
Math.floor(4.7);   // 4   : nach unten
Math.floor(-4.2);  // -5
Math.ceil(4.2);    // 5   : nach oben
Math.trunc(-4.7);  // -4  : schneidet die Nachkommastellen ab, egal welches Vorzeichen

Diese Funktionen kennen nur ganze Zahlen. Um auf zwei Nachkommastellen zu runden, multiplizierst du, rundest und teilst wieder:

javascript
Math.round(1.345 * 100) / 100;   // 1.35
Math.round(1.005 * 100) / 100;   // 1    : erwartet war 1.01, siehe unten

Die Falle der binären Nachkommastellen

1.005 existiert binär nicht: Gespeichert wird 1.00499999…, und daraus wird beim Runden 1.00. Das ist kein Bug von JavaScript, sondern das Format IEEE 754, das alle Sprachen teilen. Zwei Gegenmittel:

javascript
// 1. Den Darstellungsfehler vor dem Runden korrigieren
Math.round((1.005 + Number.EPSILON) * 100) / 100;   // 1.01

// 2. Nie Nachkommastellen speichern: Preise in ganzen Cent
const prixCentimes = 1005;
(prixCentimes / 100).toFixed(2);                    // '10.05'

Das zweite ist die Regel für alles, was mit Geld zu tun hat: Ganze Cent zu addieren ergibt nie 0.30000000000000004.

toFixed(): runden für die Anzeige

javascript
(1234.5678).toFixed(2);  // '1234.57'
(2.5).toFixed(0);        // '3'
(1.005).toFixed(2);      // '1.00'  : dieselbe Binärfalle wie bei Math.round
(0.1 + 0.2).toFixed(2);  // '0.30'  : genau dafür ist es da

typeof (1.5).toFixed(2); // 'string'
(1.5).toFixed(2) + 1;    // '1.501' : Verkettung, keine Addition

toFixed gibt einen String zurück. Für die Anzeige ist das perfekt, für die Rechnung gefährlich: Wandle mit Number() um, wenn du den Wert weiterverwenden musst. Und sein Dezimaltrennzeichen ist immer ein Punkt; für das französische Komma führt der Weg über toLocaleString.

Tausendertrennzeichen und französisches Format: toLocaleString()

javascript
const n = 1234567.891;

n.toLocaleString('fr-FR');   // '1 234 567,891'
n.toLocaleString('en-US');   // '1,234,567.891'
n.toLocaleString('de-DE');   // '1.234.567,891'

(1234.5).toLocaleString('fr-FR', { minimumFractionDigits: 2 });   // '1 234,50'
(3.14159).toLocaleString('fr-FR', { maximumFractionDigits: 2 });  // '3,14'

Eine Methode, drei nationale Konventionen, keine einzige Zeile Formatierungscode. Zwei Anmerkungen:

  • Das französische Tausendertrennzeichen ist kein gewöhnliches Leerzeichen, sondern ein schmales geschütztes Leerzeichen (U+202F). Es verhindert, dass „1 234“ am Zeilenende umbricht. Musst du es entfernen, filtere mit /\s/g, nicht mit ' '.
  • Ohne Sprachargument nimmt toLocaleString() die des Browsers oder des Systems. Für eine stabile Ausgabe gibst du immer 'fr-FR' an.

Intl.NumberFormat: Währung, Prozent, Einheiten

toLocaleString ist eine Abkürzung zu Intl.NumberFormat. Das vollständige Objekt wird einmal erzeugt und wiederverwendet, was zählt, sobald du eine Liste oder eine Tabelle formatierst.

formats.js
const euros = new Intl.NumberFormat('fr-FR', { style: 'currency', currency: 'EUR' });
euros.format(1234.5);   // '1 234,50 €'

const dollars = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' });
dollars.format(1234.5); // '$1,234.50'

const pourcent = new Intl.NumberFormat('fr-FR', { style: 'percent', maximumFractionDigits: 1 });
pourcent.format(0.256); // '25,6 %'  : der Wert ist ein Verhältnis, 0.256 und nicht 25.6

const km = new Intl.NumberFormat('fr-FR', { style: 'unit', unit: 'kilometer' });
km.format(12.5);        // '12,5 km'

const go = new Intl.NumberFormat('fr-FR', { style: 'unit', unit: 'gigabyte', maximumFractionDigits: 2 });
go.format(2.35);        // '2,35 Go'

Der Währungsformatter setzt das Symbol je nach Sprache an die richtige Stelle (im Französischen dahinter, im Englischen davor), wählt die Anzahl der Nachkommastellen der Währung und fügt das geschützte Leerzeichen ein. Lauter Regeln, die du nicht mehr selbst schreiben musst.

Kompakte Notation: 1,4 k und 1 M

„1 400 Aufrufe“ als „1,4 k“ anzuzeigen oder „1 000 000“ als „1 M“ ist ein häufiger Bedarf. Die Option notation: 'compact' erledigt das, in der gewünschten Sprache.

javascript
const compact = new Intl.NumberFormat('fr-FR', { notation: 'compact' });
compact.format(1400);       // '1,4 k'
compact.format(1000000);    // '1 M'

new Intl.NumberFormat('en-US', { notation: 'compact' }).format(23000);   // '23K'
new Intl.NumberFormat('en-US', { notation: 'compact', maximumFractionDigits: 3 }).format(1230974);   // '1.231M'

new Intl.NumberFormat('fr-FR', { notation: 'compact', compactDisplay: 'long' }).format(2500000);   // '2,5 millions'

Weitere nützliche Optionen:

javascript
new Intl.NumberFormat('fr-FR', { minimumIntegerDigits: 2 }).format(7);          // '07'
new Intl.NumberFormat('fr-FR', { useGrouping: false }).format(1234567);        // '1234567'
new Intl.NumberFormat('fr-FR', { signDisplay: 'always' }).format(12);          // '+12'
new Intl.NumberFormat('en-US', { notation: 'scientific' }).format(676565765722); // '6.766E11'
new Intl.NumberFormat('fr-FR', { maximumFractionDigits: 2, roundingMode: 'halfExpand' }).format(1.005); // '1,01'

Die letzte Zeile verdient ein Wort: Intl.NumberFormat rundet ausgehend von der dezimalen Darstellung der Zahl, nicht von ihrer binären Näherung. Aus 1.005 wird tatsächlich „1,01“, wo toFixed noch „1.00“ lieferte. Ein Grund mehr, mit Intl zu formatieren statt von Hand.

Führende Nullen und andere Basen

javascript
String(7).padStart(2, '0');   // '07'  : Stunden, Minuten, Rechnungsnummern
String(7).padStart(3, '0');   // '007'

(255).toString(16);   // 'ff'        : hexadezimal, für eine CSS-Farbe
(255).toString(2);    // '11111111'  : binär

(123.456).toPrecision(4);   // '123.5'     : Gesamtzahl der signifikanten Stellen
(1234.5678).toExponential(2); // '1.23e+3'

Zwei Verhaltensweisen von toString(), die man kennen sollte: oberhalb von 1021 und unterhalb von 10−6 wechselt JavaScript in die wissenschaftliche Notation ((1e21).toString() ergibt '1e+21', (0.0000001).toString() ergibt '1e-7'). toFixed oder Intl.NumberFormat mit notation: 'standard' behalten die ausgeschriebene Form.

Die umgekehrte Operation

Ein formatierter String lässt sich nicht einfach zurückwandeln: Number('1 234,50') ergibt NaN. Du musst die Leerzeichen entfernen und das Komma ersetzen, bevor du umwandelst. Das vollständige Vorgehen steht in String in Zahl umwandeln in JavaScript.

Und Numeral.js?

Die erste Fassung dieses Artikels stellte Numeral.js vor, eine Bibliothek mit kompakter Formatsprache: numeral(50000).format('0,0') für „50,000“, '0a' für „23k“, '0.00b' für „2.35GB“, '0o' für „23rd“. Sie steckt noch in vielen Projekten und ihre Syntax ist angenehm.

javascript
import numeral from 'numeral';

numeral(50000).format('0,0');       // 50,000
numeral(23000).format('0a');        // 23k
numeral(2348895676).format('0.00b'); // 2.35GB

Aber ihre letzte Version, die 2.0.6, stammt vom März 2017 und das Projekt wird nicht mehr gepflegt: Die Locales sind eingefroren und das französische Trennzeichen ist nicht das erwartete schmale Leerzeichen. Für ein neues Projekt deckt Intl.NumberFormat dieselben Bedürfnisse ab (Tausender, kompakt, Währung, Prozent), ohne Abhängigkeit, mit den echten Konventionen jeder Sprache und ohne Gewicht im Bundle. Behalte Numeral dort, wo es schon steckt; füge es nicht mehr hinzu.

Zusammenfassung

Bedarf Werkzeug Beispiel
Runden zum Weiterrechnen Math.round(n * 100) / 100 oder ganze Cent 1.35
Runden zum Anzeigen, mit Dezimalpunkt toFixed(2) '1234.57'
Französisches Format, Tausender und Komma toLocaleString('fr-FR') '1 234 567,891'
Währung, Prozent, Einheit Intl.NumberFormat + style '1 234,50 €'
1,4 k / 1 M notation: 'compact' '1,4 k'
Führende Nullen padStart(2, '0') '07'

Häufige Fehler

Das Ergebnis von toFixed addieren (1.5).toFixed(2) + 1 ergibt „1.501“: toFixed gibt einen String zurück, die Addition verkettet. Wandle mit Number() um, wenn du weiterrechnen musst.
Ein „schulmäßiges“ Runden erwarten (1.005).toFixed(2) ergibt „1.00“, weil 1.005 binär als 1.00499999… gespeichert wird. Für kaufmännisches Runden rechne in ganzen Cent.
Das Leerzeichen aus toLocaleString('fr-FR') für ein normales halten Das französische Tausendertrennzeichen ist ein schmales geschütztes Leerzeichen (U+202F). Ein replace(' ', '') entfernt es nicht; nimm /\s/g.
Intl.NumberFormat in einer Schleife neu erzeugen Den Formatter zu bauen ist teuer. Lege ihn einmal an und rufe dann format() für jeden Wert auf.

JavaScript

Damien Flandrin Webentwickler seit 2010, Gründer von Gekkode und Email Impact. Jeder Artikel wird vor der Veröffentlichung an einem echten Projekt getestet. Kontakt
Newsletter

Neue Tests, Tutorials und Projekte, per E-Mail.

Reproduzierbare Tests, versionierter Code, datierte Ergebnisse. Niemals Spam.