Stage 1 Draft / May 19, 2026

Amount

Introduction

This specification consists of two parts:

Editor's Note

The changes proposed here are stacked on top of the Keep Trailing Zeros ECMA-402 proposal, and include calls to ECMA-402 Abstract Operations from ECMA​-262 algorithms. Where necessary, we intend to promote those semantics to ECMA​-262.

1 The Amount Object

Introduction

An Amount is an object that wraps a numeric value—as a Number, BigInt, or String—together with an optional unit (e.g., mile, kilogram, EUR, JPY, USD-per-mile). One can intuitively understand an Amount as a value that, so to speak, knows what it is measuring.

When precision options (such as fractionDigits or significantDigits) are applied, or when unit conversion is performed, finite numeric values are stored as a decimal digit string: a String in StrDecimalLiteral form. Non-finite values are stored as the Number values NaN, +∞𝔽, or -∞𝔽. Otherwise, the original JavaScript value type (Number, BigInt, or String) is retained.

Rounding a mathematical value is an important part of this spec. When we say rounding mode in this specification we simply refer to ECMA-402's definition.

1.1 Abstract Operations

1.1.1 GetOption ( options, property, type, values, default )

The abstract operation GetOption takes arguments options (an Object), property (a property key), type (boolean, string or number), values (empty or a List of ECMAScript language values), and default (required or an ECMAScript language value) and returns either a normal completion containing an ECMAScript language value or a throw completion. It extracts the value of the specified property of options, converts it to the required type, checks whether it is allowed by values if values is not empty, and substitutes default if the value is undefined. It performs the following steps when called:

  1. Let value be ? Get(options, property).
  2. If value is undefined, then
    1. If default is required, throw a RangeError exception.
    2. Return default.
  3. If type is boolean, then
    1. Set value to ToBoolean(value).
  4. Else if type is number, then
    1. Set value to ? ToNumber(value).
  5. Else,
    1. Assert: type is string.
    2. Set value to ? ToString(value).
  6. If values is not empty and values does not contain value, throw a RangeError exception.
  7. Return value.

1.1.2 GetAmountOptions ( opts )

The abstract operation GetAmountOptions takes argument opts (an Object) and returns either a normal completion containing a Record with fields [[FractionDigits]] (a non-negative integer or undefined), [[RoundingMode]] (a rounding mode), [[SignificantDigits]] (a positive integer or undefined), and [[Unit]] (a String or undefined) or a throw completion. It validates the given options (an ECMAScript object) for creating an Amount and returns a Record with slots set to appropriate marthematical values (or undefined). It performs the following steps when called:

  1. Let opts be ? GetOptionsObject(opts).
  2. Let fractionDigits be ? GetOption(opts, "fractionDigits", number, empty, undefined).
  3. Let roundingMode be ? GetOption(opts, "roundingMode", string, « "ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor", "halfExpand", "halfTrunc", "halfEven" », "halfEven").
  4. Let significantDigits be ? GetOption(opts, "significantDigits", number, empty, undefined).
  5. Let unit be ? GetOption(opts, "unit", string, empty, undefined).
  6. If fractionDigits is not undefined, then
    1. If significantDigits is not undefined, throw a RangeError exception.
    2. If fractionDigits is not an integral Number, throw a RangeError exception.
    3. Set fractionDigits to (fractionDigits).
    4. If fractionDigits is not in the inclusive interval from 0 to 100, throw a RangeError exception.
  7. Else if significantDigits is not undefined, then
    1. If significantDigits is not an integral Number, throw a RangeError exception.
    2. Set significantDigits to (significantDigits).
    3. If significantDigits is not in the inclusive interval from 1 to 21, throw a RangeError exception.
  8. If unit is the empty String, throw a RangeError exception.
  9. Return the Record { [[FractionDigits]]: fractionDigits, [[RoundingMode]]: roundingMode, [[SignificantDigits]]: significantDigits, [[Unit]]: unit }.

1.1.3 GetAmountConvertToOptions ( opts )

The abstract operation GetAmountConvertToOptions takes argument opts (an Object) and returns either a normal completion containing a Record with fields [[FractionDigits]] (a non-negative integer or undefined), [[RoundingMode]] (a rounding mode), [[SignificantDigits]] (a positive integer or undefined), and [[Unit]] (a String or undefined) or a throw completion. It validates the given options (an ECMAScript object) for converting an Amount to another Amount and returns a Record with slots set to appropriate marthematical values (or undefined). It performs the following steps when called:

  1. Let opts be ? GetOptionsObject(opts).
  2. Let fractionDigits be ? GetOption(opts, "fractionDigits", number, empty, undefined).
  3. Let roundingMode be ? GetOption(opts, "roundingMode", string, « "ceil", "floor", "expand", "trunc", "halfCeil", "halfFloor", "halfExpand", "halfTrunc", "halfEven" », "halfEven").
  4. Let significantDigits be ? GetOption(opts, "significantDigits", number, empty, undefined).
  5. Let unit be ? GetOption(opts, "unit", string, empty, undefined).
  6. If fractionDigits is not undefined, then
    1. If significantDigits is not undefined, throw a RangeError exception.
    2. If fractionDigits is not an integral Number, throw a RangeError exception.
    3. Set fractionDigits to (fractionDigits).
    4. If fractionDigits is not in the inclusive interval from 0 to 100, throw a RangeError exception.
  7. Else if significantDigits is not undefined, then
    1. If significantDigits is not an integral Number, throw a RangeError exception.
    2. Set significantDigits to (significantDigits).
    3. If significantDigits is not in the inclusive interval from 1 to 21, throw a RangeError exception.
  8. If unit is the empty String, throw a RangeError exception.
  9. Return the Record { [[FractionDigits]]: fractionDigits, [[RoundingMode]]: roundingMode, [[SignificantDigits]]: significantDigits, [[Unit]]: unit }.

1.1.4 CreateFormatterObject ( roundingMode, minFractionDigits, maxFractionDigits, minSignificantDigits, maxSignificantDigits, roundingPriority )

The abstract operation CreateFormatterObject takes arguments roundingMode (a String), minFractionDigits (a non-negative integer or undefined), maxFractionDigits (a non-negative integer or undefined), minSignificantDigits (a positive integer or undefined), maxSignificantDigits (a positive integer or undefined), and roundingPriority (a String or undefined) and returns an Object. It creates an object with the internal slots required by FormatNumericToString, configured according to the given rounding and digit options. It performs the following steps when called:

  1. Let formatter be OrdinaryObjectCreate(null, « [[MinimumFractionDigits]], [[MinimumIntegerDigits]], [[MinimumSignificantDigits]], [[MaximumFractionDigits]], [[MaximumSignificantDigits]], [[RoundingIncrement]], [[RoundingMode]], [[RoundingType]], [[TrailingZeroDisplay]] »).
  2. Set formatter.[[RoundingMode]] to roundingMode.
  3. Set formatter.[[MinimumIntegerDigits]] to 1.
  4. Set formatter.[[RoundingIncrement]] to 1.
  5. Set formatter.[[TrailingZeroDisplay]] to "auto".
  6. If minSignificantDigits is not undefined or maxSignificantDigits is not undefined, let hasSD be true; else let hasSD be false.
  7. If minFractionDigits is not undefined or maxFractionDigits is not undefined, let hasFD be true; else let hasFD be false.
  8. If hasSD is true, then
    1. If minSignificantDigits is not undefined, let resolvedMinSD be minSignificantDigits; else let resolvedMinSD be 1.
    2. If maxSignificantDigits is not undefined, let resolvedMaxSD be maxSignificantDigits; else let resolvedMaxSD be 21.
    3. Set formatter.[[MinimumSignificantDigits]] to resolvedMinSD.
    4. Set formatter.[[MaximumSignificantDigits]] to resolvedMaxSD.
  9. Else,
    1. Set formatter.[[MinimumSignificantDigits]] to 1.
    2. Set formatter.[[MaximumSignificantDigits]] to 21.
  10. If hasFD is true, then
    1. If minFractionDigits is not undefined, let resolvedMinFD be minFractionDigits; else let resolvedMinFD be 0.
    2. If maxFractionDigits is not undefined, let resolvedMaxFD be maxFractionDigits; else let resolvedMaxFD be 100.
    3. Set formatter.[[MinimumFractionDigits]] to resolvedMinFD.
    4. Set formatter.[[MaximumFractionDigits]] to resolvedMaxFD.
  11. Else,
    1. Set formatter.[[MinimumFractionDigits]] to 0.
    2. Set formatter.[[MaximumFractionDigits]] to 100.
  12. If hasSD is true and hasFD is true, then
    1. If roundingPriority is "morePrecision", set formatter.[[RoundingType]] to more-precision; else set formatter.[[RoundingType]] to less-precision.
  13. Else if hasSD is true, then
    1. Set formatter.[[RoundingType]] to significant-digits.
  14. Else,
    1. Set formatter.[[RoundingType]] to fraction-digits.
  15. Return formatter.

1.1.5 Unit Conversion Data

Unit conversion data is derived from CLDR file units.xml. As described in Unicode Technical Standard #35 Part 6 Supplemental, Conversion Data, each <convertUnit> element defines how to convert a source unit into a compatible baseUnit. An ECMAScript implementation must ignore all special conversions and support all conversions based on factor and/or offset, interpreting the value for each as an arithmetic expression with mathematical value operands (noting the respective defaults of 1 and 0 and the implicit presence of an identity mapping for each unit identified as the value of a baseUnit).

Two units are convertible if and only if they share the same baseUnit value in CLDR. A unit that appears as a baseUnit value has an implicit identity conversion (factor 1, offset 0).

Note

In factor and offset expressions, * multiplication binds more tightly than / division, and constants defined by <unitConstant> elements are valid operands.

For example:

  • <convertUnit source='celsius' baseUnit='kelvin' offset='273.15' systems="si metric"/> specifies that conversion from unit "celsius" to unit "kelvin" requires addition of 273.15.
  • <convertUnit source='fahrenheit' baseUnit='kelvin' factor='5/9' offset='2298.35/9' systems="ussystem uksystem"/> specifies that conversion from unit "fahrenheit" to unit "kelvin" requires multiplication by (5 / 9) followed by addition of (2298.35 / 9).
  • <convertUnit source='radian' baseUnit='revolution' factor='1/2*PI' systems="si metric"/> specifies that conversion from unit "radian" to unit "revolution" requires multiplication by (1 / (2 × PI)), which references <unitConstant constant="PI" value="411557987 / 131002976" status='approximate'/> and is thus equivalent to multiplication by (1 / (2 × (411557987 / 131002976))) = (131002976 / 823115974).
  • <convertUnit source='solar-mass' baseUnit='kilogram' factor='1.98847E+30' systems="astronomical"/> specifies that conversion from unit "solar-mass" to unit "kilogram" requires multiplication by (1.98847 × 1030).

1.1.6 RenderInExponentialNotation ( v, sigDigits )

The abstract operation RenderInExponentialNotation takes arguments v (a mathematical value or negative-zero) and sigDigits (a positive integer) and returns a String. It returns v rendered in canonical exponential notation with sigDigits significant digits: an optional minus sign, a mantissa with one integer digit, an optional fractional part of length sigDigits - 1, "e", an explicit sign, and an unpadded base-10 exponent. The sign of negative zero is preserved. It performs the following steps when called:

  1. Let sign be the empty String.
  2. If v is negative-zero, then
    1. Set sign to "-".
    2. Set v to 0.
  3. Else if v < 0, then
    1. Set sign to "-".
    2. Set v to -v.
  4. If v = 0, then
    1. Let exponent be 0.
    2. Let mantissaInt be 0.
  5. Else,
    1. Let exponent be the unique integer such that 10exponentv < 10exponent + 1.
    2. Let mantissaInt be v × 10sigDigits - 1 - exponent.
  6. Assert: mantissaInt is a non-negative integer with at most sigDigits decimal digits.
  7. Let intStr be the base-10 String representation of mantissaInt with no leading zeros, or "0" if mantissaInt is 0.
  8. Repeat, while the length of intStr is less than sigDigits,
    1. Set intStr to the string-concatenation of "0" and intStr.
  9. If sigDigits = 1, then
    1. Let mantissaStr be intStr.
  10. Else,
    1. Let mantissaStr be the string-concatenation of the substring of intStr from 0 to 1, ".", and the substring of intStr from 1.
  11. If exponent ≥ 0, let expSign be "+"; else let expSign be "-".
  12. Let expStr be the unique base-10 String representation of abs(exponent) with no leading zeros.
  13. Return the string-concatenation of sign, mantissaStr, "e", expSign, and expStr.
Editor's Note

TODO: this algorithm performs essentially the same work as the latter portion of Number.prototype.toExponential (the part following its initial argument-coercion and special-value handling). We should extract a shared abstract operation from that algorithm and use it both there and here, to ensure the two stay in sync.

1.1.7 GetUnitConversionFactor ( unit )

The abstract operation GetUnitConversionFactor takes argument unit (a String) and returns either a normal completion containing a Record with fields [[BaseUnit]] (a String), [[Factor]] (a mathematical value), and [[Offset]] (a mathematical value) or a throw completion. It returns the conversion data for converting unit to its base unit. It performs the following steps when called:

  1. If unit is the source of a <convertUnit> element in the unit conversion data, then
    1. Let element be that <convertUnit> element.
    2. If element has an attribute special, throw a TypeError exception.
    3. Let baseUnit be the baseUnit of element.
    4. Let factor be the mathematical value of the factor attribute of element if present, or 1 otherwise.
    5. Let offset be the mathematical value of the offset attribute of element if present, or 0 otherwise.
  2. Else if unit is a baseUnit value in the unit conversion data, then
    1. Let baseUnit be unit.
    2. Let factor be 1.
    3. Let offset be 0.
  3. Else,
    1. Throw a TypeError exception.
  4. Return the Record { [[BaseUnit]]: baseUnit, [[Factor]]: factor, [[Offset]]: offset }.
Note

The formula for converting a value in unit to its base unit is: baseValue = value × [[Factor]] + [[Offset]].

1.1.8 ConvertUnitValue ( value, sourceUnit, targetUnit )

The abstract operation ConvertUnitValue takes arguments value (a Number), sourceUnit (a String), and targetUnit (a String) and returns either a normal completion containing a Number or a throw completion. It converts value from sourceUnit to targetUnit using Number arithmetic. It performs the following steps when called:

  1. If SameValue(sourceUnit, targetUnit) is true, return value.
  2. Let sourceConv be ? GetUnitConversionFactor(sourceUnit).
  3. Let targetConv be ? GetUnitConversionFactor(targetUnit).
  4. If sourceConv.[[BaseUnit]] is not targetConv.[[BaseUnit]], throw a TypeError exception.
  5. If value is NaN, return NaN.
  6. Let sourceFactor be sourceConv.[[Factor]].
  7. Let sourceOffset be sourceConv.[[Offset]].
  8. Let targetFactor be targetConv.[[Factor]].
  9. Let targetOffset be targetConv.[[Offset]].
  10. If sourceOffset is targetOffset, then
    1. NOTE: This preserves a value of -0𝔽.
    2. Return value × 𝔽(sourceFactor / targetFactor).
  11. Return value × 𝔽(sourceFactor / targetFactor) + 𝔽((sourceOffset - targetOffset) / targetFactor).
Note

The factor term sourceFactor / targetFactor and the offset term (sourceOffsettargetOffset) / targetFactor are computed as mathematical values, and then converted to Number values for the multiplication and addition. For non-offset conversions (the vast majority), the offset term is 0 and addition is skipped in order to preserve an input value of -0𝔽.

1.2 The Amount Constructor

The Amount constructor:

  • is %Amount%.
  • is the initial value of the the "Amount" property of the global object.
  • creates and initializes a new Amount object when called as a constructor
  • may be used as the value of an extends clause of a class definition.

1.2.1 Amount ( value [ , options ] )

  1. If NewTarget is undefined, throw a TypeError exception.
  2. If value is a String, then
    1. Let parsed be ParseText(value, StringNumericLiteral).
    2. If parsed is a List of errors, throw a RangeError exception.
  3. Else if value is not a Number or a BigInt, then
    1. Throw a TypeError exception.
  4. Let validatedOpts be ? GetAmountOptions(options).
  5. Let roundingMode be validatedOpts.[[RoundingMode]].
  6. Let fractionDigits be validatedOpts.[[FractionDigits]].
  7. Let significantDigits be validatedOpts.[[SignificantDigits]].
  8. Let unit be validatedOpts.[[Unit]].
  9. If value is a String, then
    1. Let intlMV be the StringIntlMV of parsed.
    2. Let mv be intlMV.[[Value]].
    3. If mv is not-a-number, then
      1. Set value to NaN.
    4. Else if mv is positive-infinity, then
      1. Set value to +∞𝔽.
    5. Else if mv is negative-infinity, then
      1. Set value to -∞𝔽.
    6. Else,
      1. Set value to RenderInExponentialNotation(mv, intlMV.[[StringDigitCount]]).
  10. If fractionDigits is not undefined or significantDigits is not undefined, then
    1. If value is not a Number or value is a finite Number, then
      1. Let formatter be CreateFormatterObject(roundingMode, fractionDigits, fractionDigits, significantDigits, significantDigits, undefined).
      2. Let fmtMV be the MV of value.
      3. Let fmt be FormatNumericToString(formatter, fmtMV, 0).
      4. Let fmtParsed be ParseText(fmt.[[FormattedString]], StringNumericLiteral).
      5. Assert: fmtParsed is an instance of StringNumericLiteral.
      6. Let fmtIntlMV be the StringIntlMV of fmtParsed.
      7. Let value be RenderInExponentialNotation(fmtIntlMV.[[Value]], fmtIntlMV.[[StringDigitCount]]).
  11. Let O be OrdinaryObjectCreate(%Amount.prototype%, « [[AmountValue]], [[Unit]] »).
  12. Set O.[[AmountValue]] to value.
  13. Set O.[[Unit]] to unit.
  14. Return O.
Note

When no precision options are given, Number and BigInt arguments are stored directly in [[AmountValue]], preserving the original type. String arguments and values resulting from precision options are stored as a String in canonical exponential notation, preserving the precision implied by the input.

Editor's Note

We intend to move 402's FormatNumericToString, and its dependent AOs, to 262, possibly renamed.

2 Properties of the Amount Prototype

2.1 get Amount.prototype.value

This accessor property, whose set accessor function is undefined, returns the numeric value of the Amount. Its get accessor function performs the following steps when called:

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

The value may be a Number, BigInt, or String. It is a String when precision options were applied during construction or when the Amount is the result of unit conversion.

2.2 get Amount.prototype.unit

This accessor property, whose set accessor function is undefined, returns a String value (or undefined) indicating the unit that this Amount has. Its get accessor function performs the following steps when called:

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

2.3 Amount.prototype.toString ( )

This method returns a String representation of the Amount, including a unit indicator in bracket notation.

It performs the following steps when called:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[AmountValue]]).
  3. Let v be O.[[AmountValue]].
  4. Let u be O.[[Unit]].
  5. If v is a String, then
    1. Let valueStr be v.
  6. Else if v is a Number, then
    1. If v is NaN, then
      1. Let valueStr be "NaN".
    2. Else if v is +∞𝔽, then
      1. Let valueStr be "Infinity".
    3. Else if v is -∞𝔽, then
      1. Let valueStr be "-Infinity".
    4. Else,
      1. Let decimalStr be Number::toString(v, 10).
      2. Let intlMV be ! ToIntlMathematicalValue(decimalStr).
      3. Let mv be intlMV.[[Value]].
      4. If v is -0𝔽, set mv to negative-zero.
      5. Let valueStr be RenderInExponentialNotation(mv, intlMV.[[StringDigitCount]]).
  7. Else,
    1. Assert: v is a BigInt.
    2. Let digits be BigInt::toString(v, 10).
    3. Let intlMV be ! ToIntlMathematicalValue(digits).
    4. Let valueStr be RenderInExponentialNotation(intlMV.[[Value]], intlMV.[[StringDigitCount]]).
  8. If u is undefined, return the string-concatenation of "[", valueStr, and " ~]".
  9. Return the string-concatenation of "[", valueStr, " ", u, and "]".

2.4 Amount.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] )

An ECMAScript implementation that includes the ECMA-402 Internationalization API must implement this method as specified in the ECMA-402 specification. If an ECMAScript implementation does not include the ECMA-402 API the following specification of this method is used:

It performs the following steps when called:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[AmountValue]]).
  3. Return ? Call(%Amount.prototype.toString%, O, « »).

The meanings of the optional parameters to this method are defined in the ECMA-402 specification; implementations that do not include ECMA-402 support must not use those parameter positions for anything else.

2.5 Amount.prototype.convertTo ( options )

This method returns a new Amount whose value is the result of converting this Amount’s value from its current unit to a target unit. The target unit is specified by options.

It performs the following steps when called:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[AmountValue]]).
  3. Let sourceUnit be O.[[Unit]].
  4. If sourceUnit is undefined, throw a TypeError exception.
  5. Let validatedOpts be ? GetAmountConvertToOptions(options).
  6. Let targetUnit be validatedOpts.[[Unit]].
  7. If targetUnit is undefined, throw a TypeError exception.
  8. Let roundingMode be validatedOpts.[[RoundingMode]].
  9. Let fractionDigits be validatedOpts.[[FractionDigits]].
  10. Let significantDigits be validatedOpts.[[SignificantDigits]].
  11. Let v be O.[[AmountValue]].
  12. If v is a Number, then
    1. Let sourceValue be v.
  13. Else if v is a BigInt, then
    1. Let sourceValue be 𝔽((v)).
  14. Else,
    1. Assert: v is a String.
    2. Let sourceValue be StringToNumber(v).
  15. Let convertedValue be ? ConvertUnitValue(sourceValue, sourceUnit, targetUnit).
  16. Let result be OrdinaryObjectCreate(%Amount.prototype%, « [[AmountValue]], [[Unit]] »).
  17. If convertedValue is finite and (fractionDigits is not undefined or significantDigits is not undefined), then
    1. Let formatter be CreateFormatterObject(roundingMode, fractionDigits, fractionDigits, significantDigits, significantDigits, undefined).
    2. Let intlMV be ! ToIntlMathematicalValue(convertedValue).
    3. Let formatted be FormatNumericToString(formatter, intlMV.[[Value]], 0).
    4. Let formattedMV be ! ToIntlMathematicalValue(formatted.[[FormattedString]]).
    5. Set result.[[AmountValue]] to RenderInExponentialNotation(formattedMV.[[Value]], formattedMV.[[StringDigitCount]]).
  18. Else,
    1. Set result.[[AmountValue]] to convertedValue.
  19. Set result.[[Unit]] to targetUnit.
  20. Return result.

3 Amendments to the ECMAScript® 2024 Internationalization API Specification

Editor's Note

This section lists amendments which must be made to ECMA-402, the ECMAScript® 2024 Internationalization API Specification. Text to be added is marked like this, and text to be deleted is marked like this. Blocks of unmodified text between modified sections are marked by [...].

3.1 Properties of the Amount Prototype Object

3.1.1 Amount.prototype.toLocaleString ( [ locales [ , options ] ] )

This definition supersedes the definition provided in es2025, 2.4.

This function performs the following steps when called:

  1. Let O be the this value.
  2. Perform ? RequireInternalSlot(O, [[AmountValue]]).
  3. Let v be O.[[AmountValue]].
  4. If v is a String, then
    1. Let numValue be v.
  5. Else if v is a BigInt, then
    1. Let numValue be BigInt::toString(v, 10).
  6. Else,
    1. Assert: v is a Number.
    2. Let numValue be v.
  7. Let unit be O.[[Unit]].
  8. Let mergedOptions be OrdinaryObjectCreate(null).
  9. If options is not undefined, then
    1. Let optionsObj be ? GetOptionsObject(options).
    2. Perform ? CopyDataProperties(mergedOptions, optionsObj, « »).
  10. If unit is not undefined, then
    1. If ? HasProperty(mergedOptions, "style") is false, then
      1. If IsWellFormedCurrencyCode(unit) is true, then
        1. Perform ! CreateDataPropertyOrThrow(mergedOptions, "style", "currency").
        2. If ? HasProperty(mergedOptions, "currency") is false, then
          1. Perform ! CreateDataPropertyOrThrow(mergedOptions, "currency", unit).
      2. Else,
        1. Perform ! CreateDataPropertyOrThrow(mergedOptions, "style", "unit").
        2. If ? HasProperty(mergedOptions, "unit") is false, then
          1. Perform ! CreateDataPropertyOrThrow(mergedOptions, "unit", unit).
  11. Let numberFormat be ? Construct(%Intl.NumberFormat%, « locales, mergedOptions »).
  12. Return FormatNumeric(numberFormat, numValue).

3.2 Amendment to the Normative References of ECMA-402

Editor's Note

The following entry is to be added to ECMA-402's list of referenced parts of Unicode Technical Standard #35.

4 NumberFormat Objects

4.1 Abstract Operations for NumberFormat Objects

4.1.1 GetNumberFormatPattern ( numberFormat, x )

The abstract operation GetNumberFormatPattern takes arguments numberFormat (an Intl.NumberFormat) and x (an Intl mathematical value) and returns either a normal completion containing a String or a throw completion. It considers the resolved unit-related options in the number format object along with the final scaled and rounded number being formatted (an Intl mathematical value) and returns a pattern, a String value as described in 16.2.3. It performs the following steps when called:

  1. Let resolvedLocaleData be numberFormat.[[LocaleData]].
  2. Let patterns be resolvedLocaleData.[[patterns]].
  3. Assert: patterns is a Record (see 16.2.3).
  4. Let style be numberFormat.[[Style]].
  5. If style is "percent", then
    1. Set patterns to patterns.[[percent]].
  6. Else if style is "unit", then
    1. Let unit be numberFormat.[[Unit]].
    2. Let fmtUnit be x.[[Unit]].
    3. If fmtUnit is not undefined and SameValueNonNumber(unit, fmtUnit) is false, then
      1. If unit is not undefined, throw a TypeError exception.
      2. Set unit to fmtUnit.
    4. Else if unit is undefined, then
      1. Throw a TypeError exception.
    5. Let unitDisplay be numberFormat.[[UnitDisplay]].
    6. Set patterns to patterns.[[unit]].
    7. If patterns doesn't have a field [[<unit>]], then
      1. Set unit to "fallback".
    8. Set patterns to patterns.[[<unit>]].
    9. Set patterns to patterns.[[<unitDisplay>]].
  7. Else if style is "currency", then
    1. Let currency be numberFormat.[[Currency]].
    2. Let fmtCurrency be x.[[Currency]].
    3. If fmtCurrency is not undefined and SameValueNonNumber(currency, fmtCurrency) is false, then
      1. If currency is not undefined, throw a TypeError exception.
      2. Set currency to fmtCurrency.
    4. Else if currency is undefined, then
      1. Throw a TypeError exception.
    5. Let currencyDisplay be numberFormat.[[CurrencyDisplay]].
    6. Let currencySign be numberFormat.[[CurrencySign]].
    7. Set patterns to patterns.[[currency]].
    8. If patterns doesn't have a field [[<currency>]], then
      1. Set currency to "fallback".
    9. Set patterns to patterns.[[<currency>]].
    10. Set patterns to patterns.[[<currencyDisplay>]].
    11. Set patterns to patterns.[[<currencySign>]].
  8. Else,
    1. Assert: style is "decimal".
    2. Set patterns to patterns.[[decimal]].
  9. If x is negative-infinity, then
    1. Let category be negative-non-zero.
  10. Else if x is negative-zero, then
    1. Let category be negative-zero.
  11. Else if x is not-a-number, then
    1. Let category be positive-zero.
  12. Else if x is positive-infinity, then
    1. Let category be positive-non-zero.
  13. Else,
    1. Assert: x is a mathematical value.
    2. If x < 0, then
      1. Let category be negative-non-zero.
    3. Else if x > 0, then
      1. Let category be positive-non-zero.
    4. Else,
      1. Let category be positive-zero.
  14. Let signDisplay be numberFormat.[[SignDisplay]].
  15. If signDisplay is "never", then
    1. Let pattern be patterns.[[zeroPattern]].
  16. Else if signDisplay is "auto", then
    1. If category is positive-non-zero or positive-zero, then
      1. Let pattern be patterns.[[zeroPattern]].
    2. Else,
      1. Let pattern be patterns.[[negativePattern]].
  17. Else if signDisplay is "always", then
    1. If category is positive-non-zero or positive-zero, then
      1. Let pattern be patterns.[[positivePattern]].
    2. Else,
      1. Let pattern be patterns.[[negativePattern]].
  18. Else if signDisplay is "exceptZero", then
    1. If category is positive-zero or negative-zero, then
      1. Let pattern be patterns.[[zeroPattern]].
    2. Else if category is positive-non-zero, then
      1. Let pattern be patterns.[[positivePattern]].
    3. Else,
      1. Let pattern be patterns.[[negativePattern]].
  19. Else,
    1. Assert: signDisplay is "negative".
    2. If category is negative-non-zero, then
      1. Let pattern be patterns.[[negativePattern]].
    3. Else,
      1. Let pattern be patterns.[[zeroPattern]].
  20. Return pattern.

4.1.2 SetNumberFormatUnitOptions ( intlObj, options )

The abstract operation SetNumberFormatUnitOptions takes arguments intlObj (an Intl.NumberFormat) and options (an Object) and returns either a normal completion containing unused or a throw completion. It resolves the user-specified options relating to units onto intlObj. It performs the following steps when called:

  1. Let style be ? GetOption(options, "style", string, « "decimal", "percent", "currency", "unit" », "decimal").
  2. Set intlObj.[[Style]] to style.
  3. Let currency be ? GetOption(options, "currency", string, empty, undefined).
  4. If currency is undefined, then
    1. If style is "currency", throw a TypeError exception.
  5. Else,
    1. If IsWellFormedCurrencyCode(currency) is false, throw a RangeError exception.
  6. If currency is not undefined and IsWellFormedCurrencyCode(currency) is false, throw a RangeError exception.
  7. Let currencyDisplay be ? GetOption(options, "currencyDisplay", string, « "code", "symbol", "narrowSymbol", "name" », "symbol").
  8. Let currencySign be ? GetOption(options, "currencySign", string, « "standard", "accounting" », "standard").
  9. Let unit be ? GetOption(options, "unit", string, empty, undefined).
  10. If unit is undefined, then
    1. If style is "unit", throw a TypeError exception.
  11. Else,
    1. If IsWellFormedUnitIdentifier(unit) is false, throw a RangeError exception.
  12. If unit is not undefined and IsWellFormedUnitIdentifier(unit) is false, throw a RangeError exception.
  13. Let unitDisplay be ? GetOption(options, "unitDisplay", string, « "short", "narrow", "long" », "short").
  14. If style is "currency", then
    1. If currency is not undefined, set Set intlObj.[[Currency]] to the ASCII-uppercase of currency.
    2. Set intlObj.[[CurrencyDisplay]] to currencyDisplay.
    3. Set intlObj.[[CurrencySign]] to currencySign.
  15. If style is "unit", then
    1. If unit is not undefined, Set set intlObj.[[Unit]] to unit.
    2. Set intlObj.[[UnitDisplay]] to unitDisplay.
  16. Return unused.

5 Normative References

Copyright & Software License

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.