NOTE: The other file in the gist shows the same but using "significant digits" rather than "fractional digits". After trying to use it, I noticed that almost always we want to start counting fro mthe units and not from the most significant digit.
A "numeric with precision" is a pair consisting of a numeric value and an indication of how many of its digits are significant. The number of digits is counted starting from the units: 1
means the first digit after the dot, 0
means that we have an integer, -1
means that we have a multiple of 10.
It can be obtained from a numeric value (wether it's decimal, float, or bigint) by "decorating it":
new Decimal("123.45").withFractionalDigits(1);
123.45.withFractionalDigits(1);
For any numeric type, x.withFractionalDigits(y).toString()
is equivalent to x.toFixed(y)
. There is also a x.withFractionalDigits(y).toLocaleString()
.
Given a "numeric with precision", it's possible to separately extract the numeric value that it contains and the associated precision:
123.41.withFractionalDigits(1).valueOf(); // 123.4
123.41.withFractionalDigits(1).numericObject; // Number { 123.4 }
123.41.withFractionalDigits(1).fractionalDigits; // 1
new Decimal("123.41").withFractionalDigits(-1).valueOf(); // TypeError (because decimal primitives are hidden)
new Decimal("123.41").withFractionalDigits(-1).numericObject; // Decimal { 12e1 }
new Decimal("123.41").withFractionalDigits(-1).fractionalDigits; // -1
"numeric with precision"s don't have any built-in arithmetic operations. Instead, developers must perform arithmetics on the underlying value, and then attatch the correct precision based on the error propagation theory that they are following.
When Intl.NumberFormat
and Intl.PluralRules
receive a "numeric with precision", they must default to using the encoded precision for minimumFractionalDigits
and maximumFractionalDigits
.
Rounding
When doing .withFractionalDigits(1)
of 0.123
, it should probably be rounded to loose the 2
and the 3
. How does this rounding happen? Like Math.round()
? Or should it be configurable?
How should Intl detect that an object is a "numeric with precision"?
Can it check an internal slot? Should this use a protocol (obj[Symbol.numericAndPrecision]() -> { numericObject, fractionalDigits }
)? Using a protocol would allow developers to create their own "numeric with precision" implementations that automatically do error propagation the way they want them to.
- "Numeric with precision" can itself have a
.withFractionalDigits()
method, to reduce (or also expand?) the precision of the underlying value. - There can be a
Decimal.parseWithFractionalDigits()
,Number.parseWithFractionalDigits()
that parse a number from a string and preserve the info about the precision, returning a "number with precision" object.
class NumericWithPrecision {
#numericObject;
#fractionalDigits;
constructor(numericObject, fractionalDigits) {
this.#numericObject = numericObject;
this.#fractionalDigits = fractionalDigits;
}
get numericObject() { return this.#numericObject }
get fractionalDigits() { return this.#fractionalDigits }
valueOf() { return this.#numericObject.valueOf() }
toString() {
return this.#numericObject.toString() // do some adjustment to represent the precision properly, probably
}
toLocaleString(lang, options) {
return this.#numericObject.toLocaleString(lang, {
minimumFractionalDigits: this.#fractionalDigits,
maximumFractionalDigits: this.#fractionalDigits,
...options
});
}
}
Decimal.prototype.withFractionalDigits = function (fractionalDigits) {
return new NumericWithPrecision(this, fractionalDigits)
}
Number.prototype.withFractionalDigits = function (fractionalDigits) {
return new NumericWithPrecision(Object(this), fractionalDigits)
}
@nicolo-ribaudo this seems like a good place to start. I am wondering whether the Decimal champions would want to take this idea as a dependency and also introduce
AccountingDecimal
orDecimalWithPrecision
that does provide math prototype methods for automatic propagation of precision backed by the IEEE Decimal internal representation and its accounting-oriented rules.