Skip to content

Commit

Permalink
Normative: Support BigInt in NumberFormat and toLocaleString (#236)
Browse files Browse the repository at this point in the history
This patch brings Intl.NumberFormat support to BigInt, and
adds a BigInt.prototype.toLocaleString method based on it.

The design here is to include overloading between BigInt and Number
as arguments for the format and formatToParts methods based on
ToNumeric. This means that, for example, string arguments are
cast to Number, rather than BigInt. This design preserves
compatibility and consistency with operators like unary -

This definition permits options in the NumberFormat to force
decimal places, e.g., 1n formatting as 1.00000 if the minimum
fractional digits is 5. Alternative semantics would be to
throw an exception in this case.

For the algorithm text itself: the specification algorithms
ToRawPrecision and ToRawFixed are now used for both Numbers
and BigInts. Given the ECMAScript specification's use of implicit
coercisions between Numbers and mathematical values, I believe
that this is valid without any special changes; the phrasing
may change in the future [1].

ICU4C-based implementations of ECMAScript can use
LocalizedNumberFormatter::formatDecimal [2] or
unum_formatDecimal [3] to implement the algorithms in this patch.

[1] tc39/ecma262#1135
[2] http://icu-project.org/apiref/icu4c/classicu_1_1number_1_1LocalizedNumberFormatter.html#a29cd3d107b784496e19175ce0115f26f
[3] http://icu-project.org/apiref/icu4c/unum_8h.html#a59870a322f012dc1b9d99cf8a7b708f1

Closes #218
  • Loading branch information
littledan authored and leobalter committed Jun 13, 2019
1 parent d25484c commit c682e90
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 19 deletions.
30 changes: 29 additions & 1 deletion spec/locale-sensitive-functions.html
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,35 @@ <h1>Number.prototype.toLocaleString ( [ _locales_ [ , _options_ ] ] )</h1>
<emu-alg>
1. Let _x_ be ? thisNumberValue(*this* value).
1. Let _numberFormat_ be ? Construct(%NumberFormat%, &laquo; _locales_, _options_ &raquo;).
1. Return FormatNumber(_numberFormat_, _x_).
1. Return FormatNumeric(_numberFormat_, _x_).
</emu-alg>

</emu-clause>
</emu-clause>


<emu-clause id="sup-properties-of-the-bigint-prototype-object">
<h1>Properties of the BigInt Prototype Object</h1>

<p>
The following definition(s) refer to the abstract operation thisBigIntValue as defined in ES2019, <emu-xref href="#sec-properties-of-the-bigint-prototype-object"></emu-xref>.
</p>

<emu-clause id="sup-bigint.prototype.tolocalestring">
<h1>BigInt.prototype.toLocaleString ( [ _locales_ [ , _options_ ] ] )</h1>

<p>
This definition supersedes the definition provided in ES2019, <emu-xref href="#sec-bigint.prototype.tolocalestring"></emu-xref>.
</p>

<p>
When the `toLocaleString` method is called with optional arguments _locales_ and _options_, the following steps are taken:
</p>

<emu-alg>
1. Let _x_ be ? thisBigIntValue(*this* value).
1. Let _numberFormat_ be ? Construct(%NumberFormat%, &laquo; _locales_, _options_ &raquo;).
1. Return FormatNumeric(_numberFormat_, _x_).
</emu-alg>

</emu-clause>
Expand Down
36 changes: 18 additions & 18 deletions spec/numberformat.html
Original file line number Diff line number Diff line change
Expand Up @@ -110,20 +110,20 @@ <h1>Number Format Functions</h1>
1. Let _nf_ be _F_.[[NumberFormat]].
1. Assert: Type(_nf_) is Object and _nf_ has an [[InitializedNumberFormat]] internal slot.
1. If _value_ is not provided, let _value_ be *undefined*.
1. Let _x_ be ? ToNumber(_value_).
1. Return FormatNumber(_nf_, _x_).
1. Let _x_ be ? ToNumeric(_value_).
1. Return FormatNumeric(_nf_, _x_).
</emu-alg>

<p>
The `length` property of a Number format function is 1.
</p>
</emu-clause>

<emu-clause id="sec-formatnumberstring" aoid="FormatNumberToString">
<h1>FormatNumberToString ( _intlObject_, _x_ )</h1>
<emu-clause id="sec-formatnumberstring" aoid="FormatNumericToString">
<h1>FormatNumericToString ( _intlObject_, _x_ )</h1>

<p>
The FormatNumberToString abstract operation is called with arguments _intlObject_ (which must be an object with [[MinimumSignificantDigits]], [[MaximumSignificantDigits]], [[MinimumIntegerDigits]], [[MinimumFractionDigits]], and [[MaximumFractionDigits]] internal slots), and _x_ (which must be a Number value), and returns _x_ as a string value with digits formatted according to the five formatting parameters.
The FormatNumericToString abstract operation is called with arguments _intlObject_ (which must be an object with [[MinimumSignificantDigits]], [[MaximumSignificantDigits]], [[MinimumIntegerDigits]], [[MinimumFractionDigits]], and [[MaximumFractionDigits]] internal slots), and _x_ (which must be a Number or BigInt value), and returns _x_ as a string value with digits formatted according to the five formatting parameters.
</p>

<emu-alg>
Expand All @@ -139,7 +139,7 @@ <h1>FormatNumberToString ( _intlObject_, _x_ )</h1>
<h1>PartitionNumberPattern ( _numberFormat_, _x_ )</h1>

<p>
The PartitionNumberPattern abstract operation is called with arguments _numberFormat_ (which must be an object initialized as a NumberFormat) and _x_ (which must be a Number value), interprets _x_ as a numeric value, and creates the corresponding parts according to the effective locale and the formatting options of _numberFormat_. The following steps are taken:
The PartitionNumberPattern abstract operation is called with arguments _numberFormat_ (which must be an object initialized as a NumberFormat) and _x_ (which must be a Number or BigInt value), interprets _x_ as a numeric value, and creates the corresponding parts according to the effective locale and the formatting options of _numberFormat_. The following steps are taken:
</p>

<emu-alg>
Expand All @@ -164,12 +164,12 @@ <h1>PartitionNumberPattern ( _numberFormat_, _x_ )</h1>
1. If _x_ is *NaN*, then
1. Let _n_ be an implementation- and locale-dependent (ILD) String value indicating the *NaN* value.
1. Append a new Record { [[Type]]: `"nan"`, [[Value]]: _n_ } as the last element of _result_.
1. Else if _x_ is not a finite Number,
1. Else if _x_ is not a finite Number or BigInt,
1. Let _n_ be an ILD String value indicating infinity.
1. Append a new Record { [[Type]]: `"infinity"`, [[Value]]: _n_ } as the last element of _result_.
1. Else,
1. If _numberFormat_.[[Style]] is `"percent"`, let _x_ be 100 × _x_.
1. Let _n_ be FormatNumberToString(_numberFormat_, _x_).
1. Let _n_ be FormatNumericToString(_numberFormat_, _x_).
1. If the _numberFormat_.[[NumberingSystem]] matches one of the values in the `"Numbering System"` column of <emu-xref href="#table-numbering-system-digits"></emu-xref> below, then
1. Let _digits_ be a List whose 10 String valued elements are the UTF-16 string representations of the 10 _digits_ specified in the `"Digits"` column of the matching row in <emu-xref href="#table-numbering-system-digits"></emu-xref>.
1. Replace each _digit_ in _n_ with the value of _digits_[_digit_].
Expand Down Expand Up @@ -335,11 +335,11 @@ <h1>PartitionNumberPattern ( _numberFormat_, _x_ )</h1>
</emu-note>
</emu-clause>

<emu-clause id="sec-formatnumber" aoid="FormatNumber">
<h1>FormatNumber( _numberFormat_, _x_ )</h1>
<emu-clause id="sec-formatnumber" aoid="FormatNumeric">
<h1>FormatNumeric( _numberFormat_, _x_ )</h1>

<p>
The FormatNumber abstract operation is called with arguments _numberFormat_ (which must be an object initialized as a NumberFormat) and _x_ (which must be a Number value), and performs the following steps:
The FormatNumeric abstract operation is called with arguments _numberFormat_ (which must be an object initialized as a NumberFormat) and _x_ (which must be a Number or BigInt value), and performs the following steps:
</p>

<emu-alg>
Expand All @@ -351,11 +351,11 @@ <h1>FormatNumber( _numberFormat_, _x_ )</h1>
</emu-alg>
</emu-clause>

<emu-clause id="sec-formatnumbertoparts" aoid="FormatNumberToParts">
<h1>FormatNumberToParts( _numberFormat_, _x_ )</h1>
<emu-clause id="sec-formatnumbertoparts" aoid="FormatNumericToParts">
<h1>FormatNumericToParts( _numberFormat_, _x_ )</h1>

<p>
The FormatNumberToParts abstract operation is called with arguments _numberFormat_ (which must be an object initialized as a NumberFormat) and _x_ (which must be a Number value), and performs the following steps:
The FormatNumericToParts abstract operation is called with arguments _numberFormat_ (which must be an object initialized as a NumberFormat) and _x_ (which must be a Number or BigInt value), and performs the following steps:
</p>

<emu-alg>
Expand All @@ -376,7 +376,7 @@ <h1>FormatNumberToParts( _numberFormat_, _x_ )</h1>
<h1>ToRawPrecision( _x_, _minPrecision_, _maxPrecision_ )</h1>

<p>
When the ToRawPrecision abstract operation is called with arguments _x_ (which must be a finite non-negative number), _minPrecision_, and _maxPrecision_ (both must be integers between 1 and 21), the following steps are taken:
When the ToRawPrecision abstract operation is called with arguments _x_ (which must be a finite non-negative Number or BigInt), _minPrecision_, and _maxPrecision_ (both must be integers between 1 and 21), the following steps are taken:
</p>

<emu-alg>
Expand Down Expand Up @@ -410,7 +410,7 @@ <h1>ToRawPrecision( _x_, _minPrecision_, _maxPrecision_ )</h1>
<h1>ToRawFixed( _x_, _minInteger_, _minFraction_, _maxFraction_ )</h1>

<p>
When the ToRawFixed abstract operation is called with arguments _x_ (which must be a finite non-negative number), _minInteger_ (which must be an integer between 1 and 21), _minFraction_, and _maxFraction_ (which must be integers between 0 and 20), the following steps are taken:
When the ToRawFixed abstract operation is called with arguments _x_ (which must be a finite non-negative Number or BigInt), _minInteger_ (which must be an integer between 1 and 21), _minFraction_, and _maxFraction_ (which must be integers between 0 and 20), the following steps are taken:
</p>

<emu-alg>
Expand Down Expand Up @@ -626,8 +626,8 @@ <h1>Intl.NumberFormat.prototype.formatToParts ( _value_ )</h1>
1. Let _nf_ be the *this* value.
1. If Type(_nf_) is not Object, throw a *TypeError* exception.
1. If _nf_ does not have an [[InitializedNumberFormat]] internal slot, throw a *TypeError* exception.
1. Let _x_ be ? ToNumber(_value_).
1. Return ? FormatNumberToParts(_nf_, _x_).
1. Let _x_ be ? ToNumeric(_value_).
1. Return ? FormatNumericToParts(_nf_, _x_).
</emu-alg>
</emu-clause>

Expand Down

0 comments on commit c682e90

Please sign in to comment.