Stage -1 Draft / April 11, 2025

Decimal with precision

21 Numbers and Dates

21.1 Decimal with precision

A "Decimal with precision" is an object that specifies how a Decimal object can be enriched with additional information about its precision and how that additional precision can be presented. Decimal with precision objects are not instantiable; they are created, instead, by calling the Decimal.prototype.withSignificantDigits method.

21.1.1 The %DecimalWithPrecisionPrototype% Object

The %DecimalWithPrecisionPrototype% object:

  • has properties that are inherited by all WithSignificantDigits Objects.
  • is an ordinary object.
  • has a [[Prototype]] internal slot whose value is Object.prototype.
  • has the following properties:

21.1.1.1 CreateDecimalWithPrecisionObject ( decimal, significantDigits )

The abstract operation CreateDecimalWithPrecisionObject takes arguments decimal (a Decimal128 object) and significantDigits (an integer between 0 and 34 inclusive) and returns an Object. It performs the following steps when called:

  1. Let obj be OrdinaryObjectCreate(%DecimalWithPrecisionPrototype%, « [[DecimalObject]], [[SignificantDigits]] »).
  2. Set obj.[[DecimalObject]] to decimal.
  3. Set obj.[[SignificantDigits]] to significantDigits.
  4. Return obj.

21.1.1.2 get %DecimalWithPrecisionPrototype%.decimal

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[DecimalObject]]).
  3. Return O.[[DecimalObject]].

21.1.1.3 get %DecimalWithPrecisionPrototype%.significantDigits

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[SignificantDigits]]).
  3. Return 𝔽(O.[[SignificantDigits]]).

21.1.1.4 %DecimalWithPrecisionPrototype%.toString ( )

This function performs the following steps when called:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[DecimalObject]]).
  3. Return ? Call(%Decimal.prototype.toPrecision%, O.[[DecimalObject]], « 𝔽(O.[[SignificantDigits]]) »).

21.1.1.5 %DecimalWithPrecisionPrototype%.toLocaleString ( [ locales [ , options ] ] )

Editor's Note

This will be defined in ECMA-402

This function performs the following steps when called:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[SignificantDigits]]).
  3. Let number be ! ToIntlMathematicalValue(O.[[DecimalObject]]).
  4. Let numberFormat be ? Construct(%Intl.NumberFormat%, « locales , options »).
  5. Return FormatNumeric(numberFormat, number, O.[[SignificantDigits]]).

21.2 Changes to the Decimal128 prototype

21.2.1 Decimal.prototype.withSignificantDigits ( significantDigits )

This method performs the following steps when called:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[Decimal128Data]]).
  3. If significantDigits is not a Number, throw a TypeError exception.
  4. If significantDigits is NaN𝔽, +∞𝔽, or +∞𝔽, throw a RangeError exception.
  5. If (significantDigits) is not an non-negative integer, throw a TypeError exception.
  6. If (significantDigits) > 34, throw a TypeError exception.
  7. Return CreateDecimalWithPrecisionObject(O, significantDigits).

21.2.2 Decimal.prototype.withFractionalDigits ( fractionalDigits )

This method performs the following steps when called:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[Decimal128Data]]).
  3. If fractionalDigits is not a Number, throw a TypeError exception.
  4. If fractionalDigits is NaN𝔽, +∞𝔽, or +∞𝔽, throw a RangeError exception.
  5. If (fractionalDigits) is not an integer, throw a TypeError exception.
  6. Let significantDigits be fractionalDigits + ceil(log10(abs(O.[[Decimal128Data]]))).
  7. If significantDigits < 0 or significantDigits > 34, throw a TypeError exception.
  8. Return CreateDecimalWithPrecisionObject(O, significantDigits).

100 Changes to ECMA-402

100.1 NumberFormat Objects

100.1.1 Number Format Functions

A Number format function is an anonymous built-in function that has a [[NumberFormat]] internal slot.

When a Number format function F is called with optional argument value, the following steps are taken:

  1. Let nf be F.[[NumberFormat]].
  2. Assert: nf is an Object and nf has an [[InitializedNumberFormat]] internal slot.
  3. If value is not provided, let value be undefined.
  4. Let x be ? ToIntlMathematicalValue(value).
  5. If value is an Object with a [[SignificantDigits]] internal slot, then
    1. Return FormatNumeric(nf, x, value.[[SignificantDigits]]).
  6. Return FormatNumeric(nf, x).

The "length" property of a Number format function is 1𝔽.

100.1.2 SetNumberFormatDigitOptions ( intlObj, options, mnfdDefault, mxfdDefault, notation )

The abstract operation SetNumberFormatDigitOptions takes arguments intlObj (an Object), options (an Object), mnfdDefault (an integer), mxfdDefault (an integer), and notation (a String) and returns either a normal completion containing unused or a throw completion. It populates the internal slots of intlObj that affect locale-independent number rounding (see 100.1.3). It performs the following steps when called:

  1. Let mnid be ? GetNumberOption(options, "minimumIntegerDigits,", 1, 21, 1).
  2. Let mnfd be ? Get(options, "minimumFractionDigits").
  3. Let mxfd be ? Get(options, "maximumFractionDigits").
  4. Let mnsd be ? Get(options, "minimumSignificantDigits").
  5. Let mxsd be ? Get(options, "maximumSignificantDigits").
  6. Set intlObj.[[MinimumIntegerDigits]] to mnid.
  7. Let roundingIncrement be ? GetNumberOption(options, "roundingIncrement", 1, 5000, 1).
  8. If roundingIncrement is not in « 1, 2, 5, 10, 20, 25, 50, 100, 200, 250, 500, 1000, 2000, 2500, 5000 », throw a RangeError exception.
  9. Let roundingMode be ? GetOption(options, "roundingMode", string, « "ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor", "halfExpand", "halfTrunc", "halfEven" », "halfExpand").
  10. Let roundingPriority be ? GetOption(options, "roundingPriority", string, « "auto", "morePrecision", "lessPrecision" », "auto").
  11. Let trailingZeroDisplay be ? GetOption(options, "trailingZeroDisplay", string, « "auto", "stripIfInteger" », "auto").
  12. NOTE: All fields required by SetNumberFormatDigitOptions have now been read from options. The remainder of this AO interprets the options and may throw exceptions.
  13. If roundingIncrement is not 1, set mxfdDefault to mnfdDefault.
  14. Set intlObj.[[RoundingIncrement]] to roundingIncrement.
  15. Set intlObj.[[RoundingMode]] to roundingMode.
  16. Set intlObj.[[TrailingZeroDisplay]] to trailingZeroDisplay.
  17. If mnsd is undefined and mxsd is undefined, let hasSd be false. Otherwise, let hasSd be true.
  18. If mnfd is undefined and mxsd is undefined, let hasFd be false. Otherwise, let hasFd be true.
  19. If hasSd is false ans hasFd is false, set intlObj.[[UseFallbackSignificantDigits]] to true.
  20. Let needSd be true.
  21. Let needFd be true.
  22. If roundingPriority is "auto", then
    1. Set needSd to hasSd.
    2. If needSd is true, or hasFd is false and notation is "compact", then
      1. Set needFd to false.
  23. If needSd is true, then
    1. If hasSd is true, then
      1. Set intlObj.[[MinimumSignificantDigits]] to ? DefaultNumberOption(mnsd, 1, 21, 1).
      2. Set intlObj.[[MaximumSignificantDigits]] to ? DefaultNumberOption(mxsd, intlObj.[[MinimumSignificantDigits]], 21, 21).
    2. Else,
      1. Set intlObj.[[MinimumSignificantDigits]] to 1.
      2. Set intlObj.[[MaximumSignificantDigits]] to 21.
  24. If needFd is true, then
    1. If hasFd is true, then
      1. Set mnfd to ? DefaultNumberOption(mnfd, 0, 100, undefined).
      2. Set mxfd to ? DefaultNumberOption(mxfd, 0, 100, undefined).
      3. If mnfd is undefined, set mnfd to min(mnfdDefault, mxfd).
      4. Else if mxfd is undefined, set mxfd to max(mxfdDefault, mnfd).
      5. Else if mnfd is greater than mxfd, throw a RangeError exception.
      6. Set intlObj.[[MinimumFractionDigits]] to mnfd.
      7. Set intlObj.[[MaximumFractionDigits]] to mxfd.
    2. Else,
      1. Set intlObj.[[MinimumFractionDigits]] to mnfdDefault.
      2. Set intlObj.[[MaximumFractionDigits]] to mxfdDefault.
  25. If needSd is false and needFd is false, then
    1. Set intlObj.[[MinimumFractionDigits]] to 0.
    2. Set intlObj.[[MaximumFractionDigits]] to 0.
    3. Set intlObj.[[MinimumSignificantDigits]] to 1.
    4. Set intlObj.[[MaximumSignificantDigits]] to 2.
    5. Set intlObj.[[RoundingType]] to more-precision.
    6. Set intlObj.[[ComputedRoundingPriority]] to "morePrecision".
  26. Else if roundingPriority is "morePrecision", then
    1. Set intlObj.[[RoundingType]] to more-precision.
    2. Set intlObj.[[ComputedRoundingPriority]] to "morePrecision".
  27. Else if roundingPriority is "lessPrecision", then
    1. Set intlObj.[[RoundingType]] to less-precision.
    2. Set intlObj.[[ComputedRoundingPriority]] to "lessPrecision".
  28. Else if hasSd is true, then
    1. Set intlObj.[[RoundingType]] to significant-digits.
    2. Set intlObj.[[ComputedRoundingPriority]] to "auto".
  29. Else,
    1. Set intlObj.[[RoundingType]] to fraction-digits.
    2. Set intlObj.[[ComputedRoundingPriority]] to "auto".
  30. If roundingIncrement is not 1, then
    1. If intlObj.[[RoundingType]] is not fraction-digits, throw a TypeError exception.
    2. If intlObj.[[MaximumFractionDigits]] is not intlObj.[[MinimumFractionDigits]], throw a RangeError exception.
  31. Return unused.

100.1.3 FormatNumericToString ( intlObject, x [ , fallbackSignificantDigits ] )

The abstract operation FormatNumericToString takes arguments intlObject (an Object) and x (a mathematical value or negative-zero) and optional argument fallbackSignificantDigits (an integer) and returns a Record with fields [[RoundedNumber]] (a mathematical value or negative-zero) and [[FormattedString]] (a String). It rounds x to an Intl mathematical value according to the internal slots of intlObject. The [[RoundedNumber]] field contains the rounded result value and the [[FormattedString]] field contains a String value representation of that result formatted according to the internal slots of intlObject. It performs the following steps when called:

  1. Assert: intlObject has [[RoundingMode]], [[RoundingType]], [[MinimumSignificantDigits]], [[MaximumSignificantDigits]], [[MinimumIntegerDigits]], [[MinimumFractionDigits]], [[MaximumFractionDigits]], [[UseFallbackSignificantDigits]], [[RoundingIncrement]], and [[TrailingZeroDisplay]] internal slots.
  2. If x is negative-zero, then
    1. Let sign be negative.
    2. Set x to 0.
  3. Else,
    1. Assert: x is a mathematical value.
    2. If x < 0, let sign be negative; else let sign be positive.
    3. If sign is negative, then
      1. Set x to -x.
  4. Let minimumFD be intlObject.[[MinimumFractionDigits]].
  5. Let maximumFD be intlObject.[[MaximumFractionDigits]].
  6. If fallbackSignificantDigits is provided and is non-negative, then
    1. If minimumFDfallbackSignificantDigits and fallbackSignificantDigitsmaximumFD, then
      1. Set minimumFD to fallbackSignificantDigits.
      2. Set maximumFD to fallbackSignificantDigits.
    2. Else if intlObject.[[UseFallbackSignificantDigits]] is true, then
      1. Set minimumFD to fallbackSignificantDigits.
      2. Set maximumFD to fallbackSignificantDigits.
  7. Let unsignedRoundingMode be GetUnsignedRoundingMode(intlObject.[[RoundingMode]], sign).
  8. If intlObject.[[RoundingType]] is significant-digits, then
    1. Let result be ToRawPrecision(x, intlObject.[[MinimumSignificantDigits]], intlObject.[[MaximumSignificantDigits]], unsignedRoundingMode).
  9. Else if intlObject.[[RoundingType]] is fraction-digits, then
    1. Let result be ToRawFixed(x, intlObject.[[MinimumFractionDigits]]minimumFD, intlObject.[[MaximumFractionDigits]]maximumFD, intlObject.[[RoundingIncrement]], unsignedRoundingMode).
  10. Else,
    1. Let sResult be ToRawPrecision(x, intlObject.[[MinimumSignificantDigits]], intlObject.[[MaximumSignificantDigits]], unsignedRoundingMode).
    2. Let fResult be ToRawFixed(x, intlObject.[[MinimumFractionDigits]]minimumFD, intlObject.[[MaximumFractionDigits]]maximumFD, intlObject.[[RoundingIncrement]], unsignedRoundingMode).
    3. If intlObject.[[RoundingType]] is more-precision, then
      1. If sResult.[[RoundingMagnitude]]fResult.[[RoundingMagnitude]], then
        1. Let result be sResult.
      2. Else,
        1. Let result be fResult.
    4. Else,
      1. Assert: intlObject.[[RoundingType]] is less-precision.
      2. If sResult.[[RoundingMagnitude]]fResult.[[RoundingMagnitude]], then
        1. Let result be fResult.
      3. Else,
        1. Let result be sResult.
  11. Set x to result.[[RoundedNumber]].
  12. Let string be result.[[FormattedString]].
  13. If intlObject.[[TrailingZeroDisplay]] is "stripIfInteger" and x modulo 1 = 0, then
    1. Let i be StringIndexOf(string, ".", 0).
    2. If i is not not-found, set string to the substring of string from 0 to i.
  14. Let int be result.[[IntegerDigitsCount]].
  15. Let minInteger be intlObject.[[MinimumIntegerDigits]].
  16. If int < minInteger, then
    1. Let forwardZeros be the String consisting of minInteger - int occurrences of the code unit 0x0030 (DIGIT ZERO).
    2. Set string to the string-concatenation of forwardZeros and string.
  17. If sign is negative, then
    1. If x is 0, set x to negative-zero. Otherwise, set x to -x.
  18. Return the Record { [[RoundedNumber]]: x, [[FormattedString]]: string }.

100.1.4 PartitionNumberPattern ( numberFormat, x [ , fallbackSignificantDigits ] )

The abstract operation PartitionNumberPattern takes arguments numberFormat (an object initialized as a NumberFormat) and x (an Intl mathematical value) and optional argument fallbackSignificantDigits (an integer) and returns a List of Records with fields [[Type]] (a String) and [[Value]] (a String). It creates the parts representing the mathematical value of x according to the effective locale and the formatting options of numberFormat.

Editor's Note

PartitionNumberPattern is updated to forward fallbackSignificantDigits to FormatNumericToString.

100.1.5 FormatNumeric ( numberFormat, x [ , fallbackSignificantDigits ] )

The abstract operation FormatNumeric takes arguments numberFormat (an Intl.NumberFormat) and x (an Intl mathematical value) and optional argument fallbackSignificantDigits (an integer) and returns a String. It performs the following steps when called:

  1. If fallbackSignificantDigits is defined, then
    1. Let parts be PartitionNumberPattern(numberFormat, x, fallbackSignificantDigits).
  2. Else,
    1. Let parts be PartitionNumberPattern(numberFormat, x).
  3. Let result be the empty String.
  4. For each Record { [[Type]], [[Value]] } part of parts, do
    1. Set result to the string-concatenation of result and part.[[Value]].
  5. Return result.

100.2 PluralRules Objects

Editor's Note

Similar changes as in NumberFormat objects.

A Copyright & Software License

Copyright Notice

© 2025 Nicolò Ribaudo

Software License

All Software contained in this document ("Software") is protected by copyright and is being made available under the "BSD License", included below. This Software may be subject to third party rights (rights from parties other than Ecma International), including patent rights, and no licenses under such third party rights are granted under this license even if the third party concerned is a member of Ecma International. SEE THE ECMA CODE OF CONDUCT IN PATENT MATTERS AVAILABLE AT https://ecma-international.org/memento/codeofconduct.htm FOR INFORMATION REGARDING THE LICENSING OF PATENT CLAIMS THAT ARE REQUIRED TO IMPLEMENT ECMA INTERNATIONAL STANDARDS.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
  2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
  3. Neither the name of the authors nor Ecma International may be used to endorse or promote products derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE ECMA INTERNATIONAL "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ECMA INTERNATIONAL BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.