Formater un nombre en JavaScript : toFixed, Intl.NumberFormat, séparateur de milliers et arrondis

Arrondir à deux décimales, afficher 1 234 567,89 à la française, une devise, un pourcentage ou « 1,4 k » : les méthodes natives pour formater un nombre en JavaScript, leurs pièges, et ce qu’il reste de Numeral.js.

Comment formater et manipuler les nombres en JavaScript
Réponse rapide

Pour afficher un nombre, utilisez Intl.NumberFormat ou son raccourci toLocaleString() : (1234.5).toLocaleString('fr-FR', { minimumFractionDigits: 2 }) donne « 1 234,50 », et l’option style: 'currency' ajoute la devise. Pour arrondir sans afficher, Math.round(n * 100) / 100. toFixed(2) arrondit aussi mais renvoie une chaîne.

Un nombre se calcule en Number et s’affiche en chaîne : « 1 234,50 € », « 25,6 % », « 1,4 k vues ». Entre les deux, il faut arrondir, placer un séparateur de milliers, choisir la virgule ou le point. JavaScript fait tout cela sans bibliothèque grâce à toFixed, toLocaleString et surtout Intl.NumberFormat. Toutes les sorties ci-dessous ont été exécutées sous Node.js 22.

Arrondir un nombre

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

javascript
Math.round(2.5);   // 3   : au plus proche, .5 monte
Math.round(-2.5);  // -2  : vers +Infini, pas « loin de zéro »
Math.floor(4.7);   // 4   : vers le bas
Math.floor(-4.2);  // -5
Math.ceil(4.2);    // 5   : vers le haut
Math.trunc(-4.7);  // -4  : coupe la partie décimale, quel que soit le signe

Ces fonctions ne connaissent que l’entier. Pour arrondir à deux décimales, on multiplie, on arrondit, on divise :

javascript
Math.round(1.345 * 100) / 100;   // 1.35
Math.round(1.005 * 100) / 100;   // 1    : attendu 1.01, voir ci-dessous

Le piège des décimales binaires

1.005 n’existe pas en binaire : le nombre stocké vaut 1.00499999… et s’arrondit donc à 1.00. Ce n’est pas un bug de JavaScript mais du format IEEE 754, commun à tous les langages. Deux parades :

javascript
// 1. Corriger l'erreur de représentation avant d'arrondir
Math.round((1.005 + Number.EPSILON) * 100) / 100;   // 1.01

// 2. Ne jamais stocker de décimales : les prix en centimes entiers
const prixCentimes = 1005;
(prixCentimes / 100).toFixed(2);                    // '10.05'

La seconde est la règle pour tout ce qui touche à l’argent : additionner des centimes entiers ne produit jamais 0.30000000000000004.

toFixed() : arrondir pour afficher

javascript
(1234.5678).toFixed(2);  // '1234.57'
(2.5).toFixed(0);        // '3'
(1.005).toFixed(2);      // '1.00'  : même piège binaire que Math.round
(0.1 + 0.2).toFixed(2);  // '0.30'  : c'est justement à ça qu'il sert

typeof (1.5).toFixed(2); // 'string'
(1.5).toFixed(2) + 1;    // '1.501' : concaténation, pas addition

toFixed renvoie une chaîne. C’est parfait pour l’affichage, dangereux pour le calcul : convertissez avec Number() si vous devez réutiliser la valeur. Et son point décimal est toujours un point, pour la virgule française, passez par toLocaleString.

Séparateur de milliers et format français : 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'

Une méthode, trois conventions nationales, aucune ligne de code de formatage. Deux précisions :

  • Le séparateur de milliers français n’est pas un espace ordinaire mais une espace fine insécable (U+202F). Elle empêche la coupure « 1 234 » en fin de ligne. Si vous devez la retirer, filtrez avec /\s/g, pas avec ' '.
  • Sans argument de langue, toLocaleString() prend celle du navigateur ou du système. Pour un rendu stable, indiquez toujours 'fr-FR'.

Intl.NumberFormat : devise, pourcentage, unités

toLocaleString est un raccourci vers Intl.NumberFormat. L’objet complet se crée une fois et se réutilise, ce qui compte dès qu’on formate une liste ou un tableau.

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 %'  : la valeur est un ratio, 0.256 et 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'

Le formateur de devise place le symbole au bon endroit selon la langue (après en français, avant en anglais), choisit le nombre de décimales de la monnaie et insère l’espace insécable. Autant de règles qu’il ne faut plus coder.

Notation compacte : 1,4 k et 1 M

Afficher « 1 400 vues » en « 1,4 k » ou « 1 000 000 » en « 1 M » est un besoin courant. L’option notation: 'compact' le fait, dans la langue voulue.

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'

Autres options utiles :

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'

La dernière ligne mérite un mot : Intl.NumberFormat arrondit à partir de la représentation décimale du nombre, pas de son approximation binaire. 1.005 devient bien « 1,01 », là où toFixed donnait « 1.00 ». C’est une raison de plus de formater avec Intl plutôt qu’à la main.

Zéros initiaux et autres bases

javascript
String(7).padStart(2, '0');   // '07'  : heures, minutes, numéros de facture
String(7).padStart(3, '0');   // '007'

(255).toString(16);   // 'ff'        : hexadécimal, pour une couleur CSS
(255).toString(2);    // '11111111'  : binaire

(123.456).toPrecision(4);   // '123.5'     : nombre total de chiffres significatifs
(1234.5678).toExponential(2); // '1.23e+3'

Deux comportements de toString() à connaître : au-delà de 1021 et en dessous de 10−6, JavaScript bascule en notation scientifique ((1e21).toString() donne '1e+21', (0.0000001).toString() donne '1e-7'). toFixed ou Intl.NumberFormat avec notation: 'standard' gardent l’écriture développée.

L’opération inverse

Une chaîne formatée ne se reconvertit pas telle quelle : Number('1 234,50') vaut NaN. Il faut retirer les espaces et remplacer la virgule avant de convertir. La démarche complète est dans Convertir une chaîne en nombre en JavaScript.

Et Numeral.js ?

La première version de cet article présentait Numeral.js, une bibliothèque au format compact : numeral(50000).format('0,0') pour « 50,000 », '0a' pour « 23k », '0.00b' pour « 2.35GB », '0o' pour « 23rd ». Elle reste dans beaucoup de projets et sa syntaxe est agréable.

javascript
import numeral from 'numeral';

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

Mais sa dernière version, la 2.0.6, date de mars 2017 et le projet n’est plus maintenu : les locales sont figées et le séparateur français n’est pas l’espace fine attendue. Pour un nouveau projet, Intl.NumberFormat couvre les mêmes besoins (milliers, compact, devise, pourcentage) sans dépendance, avec les vraies conventions de chaque langue et un poids nul dans le bundle. Gardez Numeral là où il est déjà, ne l’ajoutez plus.

Récapitulatif

Besoin Outil Exemple
Arrondir pour calculer Math.round(n * 100) / 100 ou centimes entiers 1.35
Arrondir pour afficher, point décimal toFixed(2) '1234.57'
Format français, milliers et virgule toLocaleString('fr-FR') '1 234 567,891'
Devise, pourcentage, unité Intl.NumberFormat + style '1 234,50 €'
1,4 k / 1 M notation: 'compact' '1,4 k'
Zéros initiaux padStart(2, '0') '07'

Erreurs fréquentes

Additionner le résultat de toFixed (1.5).toFixed(2) + 1 donne « 1.501 » : toFixed renvoie une chaîne, l’addition concatène. Convertissez avec Number() si vous devez recalculer.
Attendre un arrondi « scolaire » (1.005).toFixed(2) vaut « 1.00 » parce que 1.005 est stocké comme 1.00499999… en binaire. Pour un arrondi financier, travaillez en centimes entiers.
Remplacer l’espace de toLocaleString('fr-FR') par un espace normal Le séparateur de milliers français est une espace fine insécable (U+202F). Un replace(' ', '') ne la retire pas, utilisez /\s/g.
Recréer Intl.NumberFormat dans une boucle La construction du formateur est coûteuse. Créez-le une fois, puis appelez format() sur chaque valeur.

JavaScript

Damien Flandrin Développeur web depuis 2010, créateur de Gekkode et d’Email Impact. Chaque article est testé sur un projet réel avant publication. Contact
Newsletter

Les nouveaux tests, tutoriels et projets, par e-mail.

Tests reproductibles, code versionné, résultats datés. Jamais de spam.