Number display contracts

Intl number formatting: precision, locale defaults and invisible separators

A number's value and its localized presentation are separate contracts. Intl.NumberFormat formats a value; it does not exchange currencies, parse localized input or make financial calculations reliable.

Content updated: · Maintained by Hung Chi-Hsueh · corrections

Keep decimal input exact before formatting

Converting 9007199254740993.25 to a JavaScript Number first loses precision. Current Intl implementations can accept its exact decimal string instead. The tool checks both format and formatToParts using large and fine-fraction test values; an unsupported runtime is blocked rather than silently converted to Number. This cannot restore digits lost before you pasted them.

The first version accepts one ASCII decimal with an optional sign, 1–100 integer digits and an optional decimal point followed by 1–20 fractional digits. Blank is not zero. Spaces, grouping separators, localized digits, exponent notation, NaN, Infinity and currency/percent symbols are rejected. Leading zeros are accepted as numeric spelling, not retained as an identifier.

Read the actual fraction defaults

Exact input does not mean every digit must appear in the output. Fraction-digit limits can round the display while leaving the input unchanged. Native currency defaults commonly differ: JPY uses zero fraction digits, USD two and KWD three. The sourced currency list does not override your browser's currency metadata. Inspect resolvedOptions instead of assuming every currency has two digits.

Percent input is a ratio: 0.125 represents 12.5%. Native percent defaults can round it to 13%; custom minimum 0 and maximum 2 retain 12.5%. The tool never divides input by 100. Custom fraction limits are 0–20 and minimum must not exceed maximum; this is a product limit, not the complete platform range. Accounting style may use parentheses for negative values, depending on locale.

Request a locale, then inspect what was resolved

Set locale and currency separately. A locale does not choose an exchange rate or translate your page. The tool canonicalizes explicit BCP 47 tags, checks supportedLocalesOf with lookup matching, and reports unsupported or duplicate rows without replacing them with the browser default. A supported regional request can still resolve to less-specific data. Unicode extensions unrelated to number formatting may be ignored; compare the requested tag with resolved locale and numberingSystem.

Browser and runtime locale data can change separators, symbols and words. A preview is evidence for the current runtime, not a cross-browser byte-equality promise. The web platform does not reliably expose its ICU/CLDR version, so the tool does not invent one. Test copied code in your target browser and server runtime; JavaScript examples use a decimal string and require exact-string support.

Copy real characters, inspect annotated parts

French grouping may contain U+202F NARROW NO-BREAK SPACE, while currency or percent patterns can contain U+00A0 NO-BREAK SPACE. Right-to-left patterns can contain intentional directional controls. Do not replace these characters with ordinary spaces just because they look similar.

formatToParts returns ordered tokens whose original values join back to the formatted output. The parts table displays escaped whitespace/control characters, code points and selected character names; those annotations are not inserted into the copied result. Inputs stay in page memory, without upload, persistence or input-bearing URLs. Copying output or code is an explicit disclosure to your clipboard; inspect it before sharing.

Example
new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 }).format("9007199254740993.25");
// 9,007,199,254,740,993.25
new Intl.NumberFormat("en-US", { style: "percent", maximumFractionDigits: 2 }).format("0.125");
// 12.5%

Data provenance

Sources