<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>
# Amount
## Stage 2 Update
### Jesse Alama (Igalia, in cooperation with Google)
### Eemeli Aro (Mozilla)
---
# Two parts
1. Changes since last time
2. Request for feedback: conversion precision
---
# Since last time
- Unit shorthand ([#126](https://github.com/tc39/proposal-amount/pull/126))
- Sequence units ([#141](https://github.com/tc39/proposal-amount/pull/141), [#161](https://github.com/tc39/proposal-amount/pull/161))
- Currency units ([#160](https://github.com/tc39/proposal-amount/pull/160))
---
# Unit shorthand
```js
new Amount(1.23, "meter")
```
same as
```js
new Amount(1.23, { unit: "meter" })
```
---
# Same shorthand on `convertTo`
```js
amt.convertTo("inch")
```
same as
```js
amt.convertTo({ unit: "inch" })
```
---
# Sequence units
```js
let a = new Amount([5, 11], "foot-and-inch");
a.value // [5, 11]
a.unit // "foot-and-inch"
```
---
# Sequence unit precision
```js
let a = new Amount([5, 11], {
unit: "foot-and-inch",
fractionDigits: 1
});
a.value // [5, "11.0"]
a.toString() // "[5 foot, 11.0 inch]"
```
Precision applies to the final part only.
Every non-final part is an integer and serializes as such ([#148](https://github.com/tc39/proposal-amount/pull/148)).
---
# Convert *to* a sequence unit
```js
let b = new Amount(1.8, "meter")
.convertTo("foot-and-inch");
b.value // [5, 10.866141732283467]
let c = new Amount(1.8, "meter")
.convertTo({ unit: "foot-and-inch", fractionDigits: 1 });
c.value // [5, "10.9"]
```
---
# Convert *from* a sequence unit
```js
let d = new Amount([5, 11], "foot-and-inch");
d.convertTo("inch").value // 71
d.convertTo("meter").value // 1.8034
```
---
# Not every sequence is a target
```js
.convertTo("foot-and-inch") // ok
.convertTo("inch-and-foot") // TypeError
.convertTo("foot-and-centimeter") // TypeError
```
We require:
1. Decreasing magnitude, and
2. Integer conversion multipliers between adjacent units
---
# Currency units
```js
new Amount("12.50", "EUR").toLocaleString("de")
// "12,50 €"
new Amount(3, "day").toLocaleString("de")
// "3 Tg."
```
A unit is a currency only if it is exactly three uppercase ASCII letters.
Lowercase three-letter units (`day`, `ton`, etc.) are no longer formatted as currencies ([#160](https://github.com/tc39/proposal-amount/pull/160)).
---
# Request for feedback
The problem: Unit conversion involving
- sequence units (`A-and-B`), or
- compound units (`X-per-Y`)
needs more math than converting among simple units.
---
# Sequence units require multiple operations
**From** a sequence: convert each part, then add.
**To** a sequence: convert to the smallest unit, then partition.
_More operations means more rounding means more chance for user-visible errors._
---
# Same issue with compound units
```js
new Amount(1, "meter-per-second")
.convertTo("mile-per-hour")
```
Two factors, composed. Not one multiply.
---
# Even more operations may be required
```js
new Amount(35, "mile-per-gallon")
.convertTo("liter-per-100-km")
```
Involves multiple multiplications and a division.
---
# The question
How much precision should the spec promise when conversion is not a single multiply?
---
# Option 1: `Number` math, plus `Math.fma`
*Proposal:* Ordinary `Number` operations, using fused multiply-add wherever it applies.
Spec currently uses mathematical values as conversion factors between two convertible simple units. This option does not promise such accuracy for sequence units.
This is as close as we can reasonably get using just Numbers.
---
# Option 2: Mathematical values
*Proposal:* Spec the exact result as a mathematical value, then convert to `Number`.
Implementations choose the how. test262 says what's true.
---
# Option 3: No non-simple conversions
*Proposal:* `convertTo` supports only simple unit to simple unit.
Sequence and compound units: `TypeError`.
No precision question to answer.
---
# Our preference: Option 1
`Number` math, plus `Math.fma` where it helps.
Which raises a follow-up question about rounding.
---
# Rounding under Option 1
`.convertTo` accepts `fractionDigits`, `significantDigits`, and `roundingMode`.
If the converted `Number` is inexact, rounding near a boundary can go the wrong way.
- **1a.** Keep _all_ nine ECMA-402 rounding modes
- **1b.** Keep _some_: the five IEEE 754-2019 modes
- **1c.** Drop rounding from `.convertTo` entirely (round-ties-to-even, used by `Number`, is always used)
---
# Should we uniformly use an Array for Amount values?
Currently, Amount `.value` is one of Number, String, BigInt, or (thanks to sequence units) an Array thereof.
_Proposal:_ Uniformly use a (frozen) Array, regardless of unit.
---
# Array-based data model
Representing values as an array:
1. Removes one axis of polymorphism from the data model
2. Offers a uniform data model for all use cases
---
# Discussion
##### A: Conversion to/from sequence and compound units:
1. `Number` math, plus `Math.fma` (our preference)
2. Mathematical values in the spec
3. No non-simple conversions
If 1: keep all rounding options (1a), some (1b), or none (1c)?
---
# Discussion (cont'd)
##### B. Uniformly use a frozen Array for `.value`?
---
# The End