<!-- Summary provided to audience: --> <style> /* make code examples suck less */ pre code { margin: 0 !important; font-size: 1.25em !important; } pre { padding: 0 !important; text-align: left !important; } .align-left { text-align: left !important; } .small-li li { font-size: 0.85em !important; } </style> <!-- .slide: data-cover --> # Decimal Stage 1 Update: Amounts Jesse Alama (Igalia, in partnership with Bloomberg & Google) jesse@igalia.com May 2025 --- ## Decimal: Exact base-10 representation & arithmetic * Many programmers expect decimal numbers in their programs to be handled as they are in "math". * Some programmers consider binary floats and rounding issues arising from (arithmetic on) them as a fact of life, with no solution. * Others are aware of the issue and use a library. --- ## What's new * We continue to discuss the `Decimal.Amount` idea in the biweekly JS numerics call, in the Decimal/Measure TC39 Matrix channel, and on issues (spanning both the decimal and measure repos). * Not asking for stage advancement today, but we believe we're getting close. --- ## `Decimal.Amount`: What? * The proposed `Decimal.Amount` class wraps a `Decimal` value with information about how precise that decimal should be understood. * Examples: (1) "2.3 with two fractional digits"; (2) "-1.759 with three significant digits"; (3) "42 with four trailing zeroes" * We thereby round out the i18n and data exchange stories for `Decimal` for use cases where preservation of *all* digits is needed. * Precision in `Decimal.Amount` is tracked separately. <small>(Recall that `Decimal` works with a canonicalized subset of IEEE 754 Decimal128 whose values have no trailing zeroes.)</small> --- ## API for Decimal.Amount * Create via a `.from` static method that takes a string * `toString`/`toLocaleString` to render the underlying Decimal * `toDecimal` to extract the underlying mathematical value * `significantDigits`, `fractionalDigits`, and `trailingZeroes` properties * `.with` methods to functionally update precision in various ways * Rounding option (default ties-to-even) --- ## Code samples --- ## Basic creation ```js let amount = Decimal.Amount.from("6.200"); amount.toString(); // "6.200" amount.significantDigits; // 4 amount.fractionalDigits; // 3 amount.trailingZeroes; // 2 ``` --- ## Functionally updating precision ```js let amount = Decimal.Amount.from("6.200"); amount.with({ significantDigit: 3 }) // "6.20" amount.with({ trailingZero: 1 }); // "6.20" amount.with({ fractionalDigit: 0}); // "6" ``` --- ## Intl.NumberFormat example ```js let formatter = new Intl.NumberFormat("de-DE"); let amount = Decimal.Amount.from("-48.136"); formatter.format(amount); // "-48,136" formatter.format( amount.with({ fractionDigit: 4 }) ); // "-48,1360" ``` --- ## Integration with the [measure proposal](https://github.com/tc39/proposal-measure) The measure proposal can be slotted in later, with a unit or currency attached to a `Decimal.Amount`: ```js let a = new Decimal.Amount("5.613"); a.with({unit: "kg" }); // 5.613 kilograms a.with({significantDigit: 5, unit: "kg"}); // 5.6130 kilograms ``` _⚠️ method names could change!_ --- ## Relationship to [intl-keep-trailing-zeros](https://github.com/eemeli/proposal-intl-keep-trailing-zeros) * The idea of preserving trailing zeroes in digit strings for Intl.{NumberFormat,PluralRules} is essentially a kind of bug fix. * We may still want `Decimal.Amount` to handle use cases where precision is needed, such as roundtripping decimal data outside of `Intl` contexts. --- ## Non-Intl code samples, 1: Stepper ```js function stepUp(a: Decimal.Amount): Decimal.Amount { let quantum = new Decimal("1").scale10(0 - a.fractionalDigits); let stepped = a.toDecimal().add(quantum(a)); return Decimal.Amount.from(stepped.toFixed( { digits: a.fractionalDigits } ) ); let a = Decimal.Amount.from("42.99"); stepUp(a); // "43.00" stepUp(a.with({ fractionalDigit: 1 })); // "43.09" ``` --- ## Non-Intl code samples, 2: Data exchange ```js let data = JSON.parse(customerData); let a = Decimal.Amount.from(data.customers[42].income); let taxOwed = computeTax(a.toDecimal()); // Decimal value cell.setValue(taxOwed.toFixed(a.fractionDigit)); ``` --- ## Conclusion * `Decimal.Amount` is a little class that completes the i18n and data exchange story for `Decimal`. * With this addition, we believe that `Decimal` addresses its motivating problems and use cases. --- ## Discussion
{"type":"slide","slideOptions":{"transition":"slide","theme":"igalia","controlsLayout":"edges","slideNumber":"c/t"}}