Formattare numeri in JavaScript: toFixed e Intl.NumberFormat

Arrotondare a due decimali, mostrare 1 234 567,89 alla francese, una valuta, una percentuale o «1,4 k»: i metodi nativi per formattare un numero in JavaScript, le loro trappole e quel che resta di Numeral.js.

Formattare i numeri in JavaScript con la libreria Numeral
Risposta rapida

Per mostrare un numero, usa Intl.NumberFormat o la sua scorciatoia toLocaleString(): (1234.5).toLocaleString('fr-FR', { minimumFractionDigits: 2 }) dà «1 234,50», e l'opzione style: 'currency' aggiunge la valuta. Per arrotondare senza mostrare, Math.round(n * 100) / 100; toFixed(2) arrotonda anche lui ma restituisce una stringa.

Un numero si calcola in Number e si mostra come stringa: «1 234,50 €», «25,6 %», «1,4 k visualizzazioni». In mezzo bisogna arrotondare, mettere un separatore delle migliaia, scegliere la virgola o il punto. JavaScript fa tutto questo senza librerie grazie a toFixed, toLocaleString e soprattutto Intl.NumberFormat. Tutte le uscite qui sotto sono state eseguite con Node.js 22.

Arrotondare un numero

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

javascript
Math.round(2.5);   // 3   : al più vicino, .5 sale
Math.round(-2.5);  // -2  : verso +Infinito, non «lontano da zero»
Math.floor(4.7);   // 4   : verso il basso
Math.floor(-4.2);  // -5
Math.ceil(4.2);    // 5   : verso l'alto
Math.trunc(-4.7);  // -4  : taglia la parte decimale, qualunque sia il segno

Queste funzioni conoscono solo l’intero. Per arrotondare a due decimali si moltiplica, si arrotonda, si divide:

javascript
Math.round(1.345 * 100) / 100;   // 1.35
Math.round(1.005 * 100) / 100;   // 1    : atteso 1.01, vedi sotto

La trappola dei decimali binari

1.005 non esiste in binario: il numero memorizzato vale 1.00499999… e quindi si arrotonda a 1.00. Non è un bug di JavaScript ma del formato IEEE 754, comune a tutti i linguaggi. Due rimedi:

javascript
// 1. Correggere l'errore di rappresentazione prima di arrotondare
Math.round((1.005 + Number.EPSILON) * 100) / 100;   // 1.01

// 2. Non memorizzare mai decimali: i prezzi in centesimi interi
const prixCentimes = 1005;
(prixCentimes / 100).toFixed(2);                    // '10.05'

Il secondo è la regola per tutto ciò che riguarda il denaro: sommare centesimi interi non produce mai 0.30000000000000004.

toFixed(): arrotondare per mostrare

javascript
(1234.5678).toFixed(2);  // '1234.57'
(2.5).toFixed(0);        // '3'
(1.005).toFixed(2);      // '1.00'  : stessa trappola binaria di Math.round
(0.1 + 0.2).toFixed(2);  // '0.30'  : serve proprio a questo

typeof (1.5).toFixed(2); // 'string'
(1.5).toFixed(2) + 1;    // '1.501' : concatenazione, non addizione

toFixed restituisce una stringa. È perfetto per la visualizzazione, pericoloso per il calcolo: converti con Number() se devi riutilizzare il valore. E il suo separatore decimale è sempre il punto; per la virgola francese, passa da toLocaleString.

Separatore delle migliaia e formato francese: 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'

Un metodo, tre convenzioni nazionali, nessuna riga di codice di formattazione. Due precisazioni:

  • Il separatore delle migliaia francese non è uno spazio ordinario ma uno spazio stretto unificatore (U+202F). Impedisce che «1 234» si spezzi a fine riga. Se devi toglierlo, filtra con /\s/g, non con ' '.
  • Senza argomento di lingua, toLocaleString() prende quella del browser o del sistema. Per un risultato stabile, indica sempre 'fr-FR'.

Intl.NumberFormat: valuta, percentuale, unità

toLocaleString è una scorciatoia verso Intl.NumberFormat. L’oggetto completo si crea una volta e si riutilizza, cosa che conta appena formatti una lista o una tabella.

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 %'  : il valore è un rapporto, 0.256 e non 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'

Il formattatore di valuta mette il simbolo al posto giusto secondo la lingua (dopo in francese, prima in inglese), sceglie il numero di decimali della moneta e inserisce lo spazio unificatore. Altrettante regole che non devi più scrivere.

Notazione compatta: 1,4 k e 1 M

Mostrare «1 400 visualizzazioni» come «1,4 k», o «1 000 000» come «1 M», è un bisogno comune. L’opzione notation: 'compact' lo fa, nella lingua che vuoi.

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'

Altre opzioni utili:

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'

L’ultima riga merita una parola: Intl.NumberFormat arrotonda a partire dalla rappresentazione decimale del numero, non dalla sua approssimazione binaria. 1.005 diventa davvero «1,01», là dove toFixed dava «1.00». È una ragione in più per formattare con Intl invece che a mano.

Zeri iniziali e altre basi

javascript
String(7).padStart(2, '0');   // '07'  : ore, minuti, numeri di fattura
String(7).padStart(3, '0');   // '007'

(255).toString(16);   // 'ff'        : esadecimale, per un colore CSS
(255).toString(2);    // '11111111'  : binario

(123.456).toPrecision(4);   // '123.5'     : numero totale di cifre significative
(1234.5678).toExponential(2); // '1.23e+3'

Due comportamenti di toString() da conoscere: oltre 1021 e sotto 10−6, JavaScript passa alla notazione scientifica ((1e21).toString()'1e+21', (0.0000001).toString()'1e-7'). toFixed o Intl.NumberFormat con notation: 'standard' mantengono la scrittura estesa.

L’operazione inversa

Una stringa formattata non si riconverte così com’è: Number('1 234,50') vale NaN. Bisogna togliere gli spazi e sostituire la virgola prima di convertire. Il procedimento completo è in Convertire una stringa in numero in JavaScript.

E Numeral.js?

La prima versione di questo articolo presentava Numeral.js, una libreria dal formato compatto: numeral(50000).format('0,0') per «50,000», '0a' per «23k», '0.00b' per «2.35GB», '0o' per «23rd». È ancora presente in molti progetti e la sua sintassi è piacevole.

javascript
import numeral from 'numeral';

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

Ma la sua ultima versione, la 2.0.6, è del marzo 2017 e il progetto non è più mantenuto: le locale sono congelate e il separatore francese non è lo spazio stretto che ci si aspetta. Per un progetto nuovo, Intl.NumberFormat copre gli stessi bisogni (migliaia, compatto, valuta, percentuale) senza dipendenze, con le convenzioni reali di ogni lingua e peso nullo nel bundle. Tieni Numeral dov’è già; non aggiungerlo più.

Esigenza Strumento Esempio
Arrotondare per calcolare Math.round(n * 100) / 100 o centesimi interi 1.35
Arrotondare per mostrare, punto decimale toFixed(2) '1234.57'
Formato francese, migliaia e virgola toLocaleString('fr-FR') '1 234 567,891'
Valuta, percentuale, unità Intl.NumberFormat + style '1 234,50 €'
1,4 k / 1 M notation: 'compact' '1,4 k'
Zeri iniziali padStart(2, '0') '07'

Errori frequenti

Sommare il risultato di toFixed (1.5).toFixed(2) + 1 dà «1.501»: toFixed restituisce una stringa e l'addizione concatena. Converti con Number() se devi ricalcolare.
Aspettarsi un arrotondamento «scolastico» (1.005).toFixed(2) vale «1.00» perché 1.005 è memorizzato come 1.00499999… in binario. Per un arrotondamento contabile, lavora in centesimi interi.
Sostituire lo spazio di toLocaleString('fr-FR') con uno spazio normale Il separatore delle migliaia francese è uno spazio stretto unificatore (U+202F). Un replace(' ', '') non lo toglie; usa /\s/g.
Ricreare Intl.NumberFormat dentro un ciclo Costruire il formattatore è costoso. Crealo una volta, poi chiama format() su ogni valore.

JavaScript

Damien Flandrin Sviluppatore web dal 2010, creatore di Gekkode e di Email Impact. Ogni articolo è testato su un progetto reale prima della pubblicazione. Contatti
Newsletter

I nuovi test, tutorial e progetti, via e-mail.

Test riproducibili, codice versionato, risultati datati. Mai spam.