
Aby wyświetlić liczbę, użyj Intl.NumberFormat albo jego skrótu toLocaleString(): (1234.5).toLocaleString('fr-FR', { minimumFractionDigits: 2 }) zwraca „1 234,50”, a opcja style: 'currency' dodaje walutę. Aby zaokrąglić bez wyświetlania, użyj Math.round(n * 100) / 100. toFixed(2) również zaokrągla, ale zwraca ciąg znaków.
Liczbę oblicza się jako Number, a wyświetla w postaci ciągu znaków: „1 234,50 €”, „25,6 %”, „1,4 k wyświetleń”. Pomiędzy tymi dwiema postaciami trzeba zaokrąglić liczbę, wstawić separator tysięcy i wybrać przecinek albo kropkę. JavaScript robi to wszystko bez żadnej biblioteki dzięki toFixed, toLocaleString, a przede wszystkim Intl.NumberFormat. Wszystkie wyniki poniżej zostały wykonane pod Node.js 22.
Zaokrąglanie liczby
Math.round, Math.floor, Math.ceil, Math.trunc
Math.round(2.5); // 3 : do najbliższej, .5 w górę
Math.round(-2.5); // -2 : w stronę +Infinity, nie „od zera”
Math.floor(4.7); // 4 : w dół
Math.floor(-4.2); // -5
Math.ceil(4.2); // 5 : w górę
Math.trunc(-4.7); // -4 : obcina część dziesiętną, niezależnie od znakuTe funkcje znają tylko liczby całkowite. Aby zaokrąglić do dwóch miejsc po przecinku, mnożymy, zaokrąglamy, dzielimy:
Math.round(1.345 * 100) / 100; // 1.35
Math.round(1.005 * 100) / 100; // 1 : oczekiwano 1.01, patrz niżejPułapka binarnych ułamków dziesiętnych
1.005 nie istnieje w zapisie binarnym: liczba przechowywana w pamięci to w rzeczywistości 1.00499999…, więc zaokrągla się do 1.00. To nie błąd JavaScriptu, lecz konsekwencja formatu IEEE 754, wspólnego dla wszystkich języków. Dwa sposoby, by sobie z tym poradzić:
// 1. Najpierw popraw błąd reprezentacji, potem zaokrąglij
Math.round((1.005 + Number.EPSILON) * 100) / 100; // 1.01
// 2. Nigdy nie przechowuj ułamków dziesiętnych: ceny trzymaj w całkowitych groszach
const prixCentimes = 1005;
(prixCentimes / 100).toFixed(2); // '10.05'Drugi sposób to reguła obowiązująca wszędzie tam, gdzie chodzi o pieniądze: dodawanie całkowitych groszy nigdy nie da w wyniku 0.30000000000000004.
toFixed(): zaokrąglanie do wyświetlenia
(1234.5678).toFixed(2); // '1234.57'
(2.5).toFixed(0); // '3'
(1.005).toFixed(2); // '1.00' : ta sama binarna pułapka co w Math.round
(0.1 + 0.2).toFixed(2); // '0.30' : właśnie do tego służy
typeof (1.5).toFixed(2); // 'string'
(1.5).toFixed(2) + 1; // '1.501' : konkatenacja, nie dodawanietoFixed zwraca ciąg znaków. To rozwiązanie doskonałe do wyświetlania, ale niebezpieczne przy obliczeniach: jeśli chcesz ponownie użyć wartości, przekonwertuj ją funkcją Number(). Jej separator dziesiętny to zawsze kropka. Francuski przecinek otrzymasz przez toLocaleString.
Separator tysięcy i francuski format zapisu: toLocaleString()
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'Jedna metoda, trzy krajowe konwencje zapisu, ani jednej linii kodu formatującego. Dwie uwagi:
- Francuski separator tysięcy to nie zwykła spacja, lecz wąska spacja niełamliwa (U+202F). Zapobiega ona rozbiciu zapisu „1 234” na końcu wiersza. Jeśli chcesz ją usunąć, filtruj wyrażeniem
/\s/g, a nie' '. - Bez podanego argumentu języka
toLocaleString()korzysta z ustawień przeglądarki lub systemu. Dla stabilnego wyniku zawsze podawaj'fr-FR'.
Intl.NumberFormat: waluta, procenty, jednostki
toLocaleString to skrót do Intl.NumberFormat. Pełny obiekt tworzy się raz i używa wielokrotnie, co ma znaczenie, gdy formatujemy listę albo tabelę.
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 %' : ta wartość to ułamek, 0.256, a nie 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'Formater walutowy umieszcza symbol we właściwym miejscu w zależności od języka (po liczbie po francusku, przed liczbą po angielsku), dobiera liczbę miejsc po przecinku właściwą dla danej waluty i wstawia spację niełamliwą. To reguły, których nie trzeba już samodzielnie programować.
Notacja kompaktowa: 1,4 k i 1 M
Wyświetlenie „1 400 wyświetleń” jako „1,4 k” albo „1 000 000” jako „1 M” to częsta potrzeba. Robi to opcja notation: 'compact', w wybranym języku.
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'Inne przydatne opcje:
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'Ostatnia linijka zasługuje na komentarz: Intl.NumberFormat zaokrągla na podstawie dziesiętnej reprezentacji liczby, a nie jej binarnego przybliżenia. 1.005 rzeczywiście staje się „1,01”, podczas gdy toFixed dawało „1.00”. To kolejny powód, by formatować liczby przez Intl, zamiast robić to ręcznie.
Zera wiodące i inne systemy liczbowe
String(7).padStart(2, '0'); // '07' : godziny, minuty, numery faktur
String(7).padStart(3, '0'); // '007'
(255).toString(16); // 'ff' : szesnastkowo, na przykład do koloru CSS
(255).toString(2); // '11111111' : binarnie
(123.456).toPrecision(4); // '123.5' : łączna liczba cyfr znaczących
(1234.5678).toExponential(2); // '1.23e+3'Warto znać dwa zachowania toString(): powyżej 1021 i poniżej 10−6 JavaScript przełącza się na zapis naukowy ((1e21).toString() zwraca '1e+21', (0.0000001).toString() zwraca '1e-7'). toFixed albo Intl.NumberFormat z opcją notation: 'standard' zachowują pełny zapis.
Operacja odwrotna
Sformatowanego ciągu znaków nie da się przekonwertować wprost: Number('1 234,50') zwraca NaN. Trzeba usunąć spacje i zamienić przecinek na kropkę przed konwersją. Pełny opis znajdziesz w artykule Konwersja ciągu znaków na liczbę w JavaScript.
A co z Numeral.js?
Pierwsza wersja tego artykułu prezentowała bibliotekę Numeral.js, w formacie kompaktowym: numeral(50000).format('0,0') dla „50,000”, '0a' dla „23k”, '0.00b' dla „2.35GB”, '0o' dla „23rd”. Wciąż jest obecna w wielu projektach, a jej składnia jest przyjemna.
import numeral from 'numeral';
numeral(50000).format('0,0'); // 50,000
numeral(23000).format('0a'); // 23k
numeral(2348895676).format('0.00b'); // 2.35GBJednak jej ostatnia wersja, 2.0.6, pochodzi z marca 2017 roku, a projekt nie jest już rozwijany: lokalizacje są zamrożone, a francuski separator nie odpowiada oczekiwanej wąskiej spacji. W nowym projekcie Intl.NumberFormat pokrywa te same potrzeby (tysiące, notacja kompaktowa, waluta, procenty) bez żadnej zależności, z prawdziwymi konwencjami każdego języka i zerowym wpływem na rozmiar paczki. Jeśli Numeral jest już w projekcie, zostaw go tam, ale nie dodawaj go w nowych.
Podsumowanie
| Potrzeba | Narzędzie | Przykład |
|---|---|---|
| Zaokrąglenie do obliczeń | Math.round(n * 100) / 100 albo całkowite grosze | 1.35 |
| Zaokrąglenie do wyświetlenia, kropka dziesiętna | toFixed(2) | '1234.57' |
| Format francuski, tysiące i przecinek | toLocaleString('fr-FR') | '1 234 567,891' |
| Waluta, procent, jednostka | Intl.NumberFormat + style | '1 234,50 €' |
| 1,4 k / 1 M | notation: 'compact' | '1,4 k' |
| Zera wiodące | padStart(2, '0') | '07' |
Częste błędy
(1.5).toFixed(2) + 1 daje „1.501”: toFixed zwraca ciąg znaków, więc dodawanie staje się konkatenacją. Przekonwertuj wynik funkcją Number(), jeśli chcesz go dalej przeliczać.(1.005).toFixed(2) daje „1.00”, ponieważ 1.005 jest przechowywane w pamięci jako 1.00499999… w zapisie binarnym. Do zaokrągleń finansowych pracuj na całkowitych groszach.toLocaleString('fr-FR') zwykłą spacją Francuski separator tysięcy to wąska spacja niełamliwa (U+202F). replace(' ', '') jej nie usunie. Użyj /\s/g.format() dla każdej wartości.

