Stage 1 Draft / April 30, 2024

Decimal

1 Scope

This is the spec text of the Decimal proposal in ECMAScript.

2 Abstract Operations

2.1 Type Conversion

2.1.1 ToString ( argument )

The abstract operation ToString takes argument argument (an ECMAScript language value) and returns either a normal completion containing a String or a throw completion. It converts argument to a value of type String. It performs the following steps when called:

  1. If argument is a String, return argument.
  2. If argument is a Symbol, throw a TypeError exception.
  3. If argument is undefined, return "undefined".
  4. If argument is null, return "null".
  5. If argument is true, return "true".
  6. If argument is false, return "false".
  7. If argument is a Number, return Number::toString(argument, 10).
  8. If argument is a BigInt, return BigInt::toString(argument, 10).
  9. If argument is a Decimal128 object, return ! Call([argument].[[toString]], argument).
  10. Assert: argument is an Object.
  11. Let primValue be ? ToPrimitive(argument, string).
  12. Assert: primValue is not an Object.
  13. Return ? ToString(primValue).

2.2 The Decimal128 Object

Introduction

Decimal128 values, as specified here, are intended represent base-10 (decimal) numbers. ECMAScript, and many other languages, default to representing numbers in base-2 (binary). The conversion of a decimal digit string to a Number is usually inexact; the Number representing the initial decimal digit string is only approximately correct.

An IEEE 754 Decimal128 value is an element of the universe of values specified by IEEE 754 standard.

Decimal128 values defined in this section are ECMAScript analogues of IEEE-754 Decimal128 values. The full spectrum of values defined by Decimal128 are available here, including NaN and positive and negative infinity. (NB: The values NaN and both infinities, available here, are distinct from the built-in NaN, Infinity and -Infinity values that arise from the 64-bit binarry IEEE arithmetic used outside of the Decimal128 universe.) Decimal128 is a floating-point format whose range of values allows up to 34 significant digits and an exponent ranging from -6143 to 6144.

Essentially all of the entities defined by the IEEE 754 Decimal128 standard exist as Decimal128 objects, with one class of exceptions: NaN. Decimal128 supports NaN. All Decimal128-NaN values in IEEE 754 Decimal128 are mapped to a NaN Decimal128 value. There may be distinct NaN Decimal128 values in the sense of object identity, but they are all supposed to behave identically. Thus, when we refer to a Decimal128 NaN, we mean any Decimal128 object whose isNaN property is true. There is no Decimal128 analogue of IEEE 754 signalling NaNs. Any arithmetic operation defined here that takes a Decimal128-NaN argument will return a Decimal128-NaN. Depending on the the operation and its arguments, a Decimal128-NaN may be returned.

Thus, we grant ourselves a mapping A from the set of IEEE Decimal128 values to Decimal128 objects. Moreover, we grant ourselves a map B from Decimal128 objects to IEEE 754 Decimal128 values. We choose a canonical quiet NaN as the IEEE 754 Decimal128 representative for all Decimal128-NaN objects, and similarly for infinities. Apart from NaNs and infinities, the mappings A and B are one-to-one.

A rounding mode is one of the values "ceil", "floor", "trunc", "halfEven", and "halfCeil".

The default rounding mode is the String value halfEven.

A digit is one of the values "0", "1", "2", "3", "4", "5", "6", "7", "8", and "9"

A decimal string is a string whose syntax is that of a sequence of digits followed by, optionally, a "." and a sequence of digits.

A signed decimal string is a decimal string optionally preceded by a "-" (dash).

An exponential string is a string whose syntax is a decimal string whose mathematical value is at least 0 but less than 9, followed by an "E", optionally a "-" (minus), and a sequence of digits.

A signed exponential string is an exponential string optionally precided by a "-" (dash).

The mathematical value of a Decimal128 object d, provided d is finite and not NaN, is the rational number coefficient of d multiplied by 10 raised to the power of the exponent of d. If d is infinite, the mathematical value of d is -∞ is d is negative and ∞ otherwise. If d is NaN, the mathematical value of d is undefined.

A Decimal128 value is said to be normal if its coefficient either (zero and has an exponent of zero) or has an exponent that is not divisible by ten. Example: 1.2 is normal, but 1.20 is not normal. In terms of decimal strings, a normal value corresponds precisely to strings whose decimal part terminates with a sequence of zeros.

2.2.1 Abstract Operations

2.2.1.1 IsIntegralDecimal128 ( argument )

The abstract operation IsIntegralDecimal128 takes argument argument (an ECMAScript language value) and returns a Boolean. It determines if argument is a finite integral Decimal128 value. It performs the following steps when called:

  1. If argument is not a Decimal128 object, return false.
  2. If [argument].[[isNaN]] is true, return false.
  3. If [argument].[[isFinite]] is false, return false.
  4. Let rounded be the result of ! Call ([argument].[[round]], 0, "trunc").
  5. Return the result of Call ([argument].[[equals]], rounded).

2.2.2 The Decimal128 Constructor

The constructor for the Decimal128 object takes a single argument. If the argument is not a String, throw a TypeError. The String argument must match one of the following four valid syntaxes:

If _x_ does not match any of these syntaxes, throw a SyntaxError.

Multiple leading zeros will be reduced to a single zero.

2.3 Properties of the Decimal128 Prototype

2.3.1 Decimal128.prototype.isNaN

A property indicating whether this value represents a Decimal128 NaN value. The possible values are true and false.

2.3.2 Decimale128.prototype.isFinite

A property indicating whether this value represents a finite value. The possible values are true and false. Decimal128 NaN values are considered finite.

2.3.3 Decimale128.prototype.significand

A property that stores the significand of this value. Its type is BigInt. This property should be a BigInt even if this value is NaN or infinite.

2.3.4 Decimale128.prototype.exponent

A property that stores the exponent of this value. Its type is Number. It should always be an integer. This property should be a Number even if this value is NaN or infinite.

2.3.5 Decimal128.prototype.abs ( )

Computes the absolute value of B of this value according to the rules specified in IEEE 754 Decimal128 and returns A of the resulting IEEE 754 Decimal128 value. Returns a fresh result, regardless of the mathematical value of this value. this value is not modified.

2.3.6 Decimal128.prototype.neg ( )

Computes the negation of B of this value Returns a fresh result, regardless of the mathematical value of this value. this value is not modified.

2.3.7 Decimal128.prototype.add ( x )

Adds B of this value and B of _x_ according to the rules specified in IEEE 754 Decimal128 and returns A of the resulting Decimal128 value. Returns a fresh result, regardless of the mathematical value of this value or _x_. Neither this value nor _x_ is modified. If, in the computation, more significant are generated than can be stored in an IEEE 754 Decimal128 value, the final digit will be consulted and the result rounded according to the default rounding mode.

2.3.8 Decimal128.prototype.subtract ( x )

Subtracts B of _x_ from B of this value according to the rules for subtraction in the IEEE 754 Decimal128 specification and returns A of the resulting IEEE Decimal128 value. Returns a fresh result, regardless of the mathematical value of _x_. Neither this value nor _x_ is modified. If, in the computation, more significant are generated than can be stored in an IEEE 754 Decimal128 value, the final digit will be consulted and the result rounded according to the default rounding mode.

2.3.9 Decimal128.prototype.multiply ( x )

Multiplies B of this value by B of _x_ according to the rules of the IEEE 754 Decimal128 specification and returns A of the resulting Decimal128 value. Returns a fresh result, regardless of the mathematical value of this value or that of _x_. Neither argument is modified. If, in the computation, more significant are generated than can be stored in an IEEE 754 Decimal128 value, the final digit will be consulted and the result rounded according to the default rounding mode.

2.3.10 Decimal128.prototype.divide ( x )

Divides B of this value by B of _y_ according to the rules of the IEEE 754 Decimal128 specification and returns A of the resulting Decimal128 value. Returns a fresh result. Neither this value nor _x_ is modified. If, in the computation, more significant are generated than can be stored in an IEEE 754 Decimal128 value, the final digit will be consulted and the result rounded according to the default rounding mode.

2.3.11 Decimal128.prototype.remainder ( x )

Computes the remainder of dividing this value by _x_. Exact division of this by _x_ is carried out until the computation stops; the residue of that computation is returned. The sign of the result is the sign of this value. Returns a fresh result.

Note

The remainder operation specified here deviates from the IEEE 754 remainder operation. We follow the the remainder operation for Number rather than IEEE 754, which specifies that, for example, the remainder 46 and 10 is -4 rather than 6.

2.3.12 Decimal128.prototype.equals ( x )

Compares the mathematical value of this value with the mathematical value of _x_, which is supposed to be a Decimal128 object. If _x_ is not a Decimal128 object, throws a TypeError. Returns true or false.

  1. If x is not a Decimal128 object, then
    1. throw a TypeError exception.
  2. If [this][[isNaN]], then
    1. If [_x_][[isNaN]] is true, return true.
    2. Return false.
  3. If [_x_][[isNaN]] is true, return false.
  4. Let _c_ be the result of calling the IEEE 754 Decimal128 compare_total procedure on this value and _x_.
  5. If _c_ is 0, return true, else return false.

2.3.13 Decimal128.prototype.lessThan ( x )

Compares the mathematical value of this value with the mathematical value of _x_, which is supposed to be a Decimal128 object. If _x_ is not a Decimal128 object, throws a TypeError. Returns true or false according as the mathematical value of this value is strictly less than the mathematical value of _x_.

  1. If x is not a Decimal128 object, then
    1. throw a TypeError exception.
  2. If [this][[isNaN]], throw a RangeError exception.
  3. If [_x_][[isNaN]], throw a RangeError exception.
  4. Let _c_ be the result of calling the IEEE 754 Decimal128 compare procedure on this value and _x_.
  5. If _c_ is -1, return true, else return false.

2.3.14 Decimal128.prototype.compare ( x )

Compares this value with of _x_ according to the specification of the IEEE 754 compare_total operation. Returns -1, 0, or 1 according as this value is greater than, equal, or less than _x_ according to the ordering specified by IEEE 754's compare_total operation.

  1. If x is not a Decimal128 object, then
    1. throw a TypeError exception.
  2. Let _c_ be the result of calling the IEEE 754 Decimal128 compare_total procedure on this value and _x_.
  3. Assert: _c_ is in « -1, 0, 1 ».
  4. Return _c_.
Note

This operation compares Decimal128 values as digit strings. Thus, for example, 1.2 and 1.20 are considered distinct values for this operation. To compare by mathematical value, see lessThan and equals.

2.3.15 Decimal128.prototype.round ( numFractionalDigits [ , mode ] )

Rounds this value according to the specified rounding mode, returning a fresh result. The result will have at most numFractionalDigits after the decimal point.

Note

If mode is neither undefined nor a String, throw a TypeError exception.

If mode is not a rounding mode, throw a RangeError exception.

2.3.16 Decimal128.prototype.toString ( [ options ] )

Renders this value as a signed decimal string. options, is defined, is supposed to be a plain object. If options has the property numDecimal128Digits and the value of that property is a non-negative Number, the return value will be a string that has exactly that many digits after the decimal point, possibly with the addition of trailing zeros.

  1. Let roundingMode be [[_options_]][roundingMode]].
  2. If roundingMode is not one of "ceil", "floor", "trunc", "halfEven", or "halfCeil", then
    1. set roundingMode to "halfEven".
  3. Let format be [[_options_]][format]].
  4. If format is not one of "decimal" or "exponential", then
    1. set format to "decimal".
  5. If format is "decimal", then
    1. return the result of calling IEEE 754's convertToDecimalCharacter with a second argument ensuring that a signed decimal string is generated. TODO convertToDecimalCharacter is unspecified, so we need to do that work here.
  6. Return the result of calling IEEE 754's convertToDecimalCharacter with a second argument ensuring that a signed exponential string is generated. TODO convertToDecimalCharacter is unspecified, so we need to do that work here.

2.3.17 Decimal128.prototype.valueOf ( x )

Throw TypeError.

3 Numbers and Dates

3.1 Number Objects

3.1.1 The Number Constructor

3.1.1.1 Number ( value )

This function performs the following steps when called:

  1. If value is present, then
    1. Let serialized be the result of ! Call([value].[[toString]], value).
    2. If value is a Decimal128 object, return ! ToNumber(serialized).
    3. Let prim be ? ToNumeric(value).
    4. If prim is a BigInt, let n be 𝔽((prim)).
    5. Otherwise, let n be prim.
  2. Else,
    1. Let n be +0𝔽.
  3. If NewTarget is undefined, return n.
  4. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%Number.prototype%", « [[NumberData]] »).
  5. Set O.[[NumberData]] to n.
  6. Return O.

3.2 BigInt Objects

3.2.1 The BigInt Constructor

3.2.1.1 BigInt ( value )

This function performs the following steps when called:

  1. If NewTarget is not undefined, throw a TypeError exception.
  2. If value is a Decimal128 object, return ? Decimal128ToBigInt(value).
  3. Let prim be ? ToPrimitive(value, number).
  4. If prim is a Number, return ? NumberToBigInt(prim).
  5. Otherwise, return ? ToBigInt(prim).

3.2.1.1.1 Decimal128ToBigInt ( number )

The abstract operation Decimal128ToBigInt takes argument number (a Decimal128 object) and returns either a normal completion containing a BigInt or a throw completion. It performs the following steps when called:

  1. If IsIntegralDecimal128(number) is false, throw a RangeError exception.
  2. Return ((number)).

A Copyright & Software License

Copyright Notice

© 2024 Jesse Alama

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.