1 of 15

Intl Number Format V3

Stage 3 Update

Shane Carr

July 2022 TC39

2 of 15

What is this?

Dozens of feature requests get filed against ECMA-402 every year.

How do we decide which ones to prioritize?

  • Multiple companies/stakeholders.
  • Already has Unicode/CLDR algorithms we can cite.
  • Not easily implemented in user land.

3 of 15

Decision Process Examples

Feature

Stakeholders

CLDR Support

Verdict

Google

Partial

No

Google, CaixaBank, Community

Yes

Yes

4 of 15

Parts of This Proposal

green text = changed since Stage 3 presentation

blue text = proposed additional changes

5 of 15

Intl.NumberFormat formatRange: Overview

Main issue: #393

Use cases:

  • Number ranges
    • What versions of Android are supported?
  • Currency ranges
    • How much does a rideshare cost?
    • What's the expected payment on my mortgage?
  • Measurement unit ranges
    • What's the gas mileage of your car?

6 of 15

Intl.NumberFormat formatRange: Details

  • Methods added:
    • Intl.NumberFormat.prototype.formatRange
    • Intl.NumberFormat.prototype.formatRangeToParts
    • Intl.PluralRules.prototype.selectRange
  • formatToParts will have { source: "startRange" }, …, like DateTimeFormat
  • Add localized approximately sign when numbers are the same: "~€3"
  • Ranges to infinity are supported, but NaN ⇒ RangeError
  • Print the range whether or not the numbers are in order (#100)

const nf = new Intl.NumberFormat("en-US", {

style: "currency",

currency: "EUR",

signDisplay: "always",

});

nf.formatRange(2.999, 3.001); // "~+€3.00"

Newly approved

7 of 15

Grouping Enum

Main issue: #367

Problem: { useGrouping } takes only true/false, but this is not expressive enough. We know options people want.

Aliases are specified for "auto" and "always" from the existing inputs undefined and true. However, the new values are considered canonical and will be returned in resolvedOptions.

Strategy

One Thousand

Ten Thousand

"min2"

1000

10,000

"auto"�undefined

all others

locale- dependent, usually 1,000

locale- dependent, usually 10,000

"always"�true

1,000

10,000

false

1000

10000

8 of 15

New Rounding/Precision Options

Main issue: #286

New options:

  • roundingPriority (details on next slide)
  • roundingIncrement = a Number in the following list: « 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1000, 2000, 2500, 5000 » (default 1)
  • trailingZeroDisplay = whether to display trailing zeros on whole numbers:
    • "auto" = current behavior. Keep trailing zeros according to minimumFractionDigits and minimumSignificantDigits.
    • "stripIfInteger" = same as "auto", but remove the fraction digits if they are all zero.

9 of 15

Rounding Priority (Details: #8)

Problem: what happens when you specify

{ maximumFractionDigits: 2, maximumSignificantDigits: 2 }

You specified two conflicting strategies [example: 4.321]:

  • Round to the hundredths place (10^-2) [example: "4.32"]
  • Round after the second significant digit [example: "4.3"]

New setting roundingPriority resolves this conflict:

  • "significantDigits" significant digits always win. [example: "4.3"] ⇒ current behavior
  • "morePrecision" the result with more precision wins. [example: "4.32"]
  • "lessPrecision" the result with less precision wins. [example: "4.3"]

10 of 15

Interpret Strings as Decimals

Main issue: #334

We already accept strings, but they get parsed as a Number, losing precision. This part of the proposal will interpret the decimal string at full precision.

How it works: a new type called Intl mathematical value: "a mathematical value together with +∞, -∞, NaN, and -0" [PR is open awaiting review: #82]

const nf = new Intl.NumberFormat("en-US");

const string = "987654321987654321";

nf.format(string);

// Current: "987,654,321,987,654,300"

// Proposed: "987,654,321,987,654,321"

11 of 15

Rounding Modes

Main issue: #419

We will adopt the following 9 rounding modes.

  • ceil (toward +∞)
  • floor (toward -∞)
  • expand (away from 0)
  • trunc (toward 0)

This set encompasses ICU, CSS, and ECMA-262 Math.

  • halfCeil (ties toward +∞)
  • halfFloor (ties toward -∞)
  • halfExpand (ties away from 0; current behavior; default)
  • halfTrunc (ties toward 0)
  • halfEven (ties toward the value with even cardinality)

12 of 15

Sign Display Negative

Main Issue: #17

We will add a new option signDisplay: "negative". The new option will behave like "auto" except that the sign will not be shown on negative zero.

var nf = new Intl.NumberFormat("en", {� signDisplay: "negative"�});�nf.format(-1.0); // -1�nf.format(-0.0); // 0 (note: "auto" produces "-0" here)�nf.format(0.0); // 0�nf.format(1.0); // 1 (note: "exceptZero" produces "+1" here)

13 of 15

Remaining Open Issues

14 of 15

Remaining Open Issues

All issues have appropriate labels. Remaining open issues that could impact observable behavior:

  • #63: Add [[Source]] to formatToParts output
    • Small issue that was missed earlier during Stage 3 advancement
  • #96: Improve algorithm for resolving minimum digits in roundingPriority
    • Suggestion from Philip Chimento; only affects call sites with minimum fraction digits
  • #98: Limit exponent and range of intl mathematical values
    • Add sane limits to make the proposal more implementable (larger than the limits of a double)

15 of 15

Discussion