Skip to content

tc39/proposal-measure

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

48 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Representing Measures

Stage: 1

Champion: Ben Allen @ben-allen

Author: Ben Allen @ben-allen

Goals and needs

Modeling amounts with a precision is useful for any task that involves physical quantities. It can also be useful for other types of real-world amounts, such as currencies. We propose creating a new object for representing amounts, and for producing formatted string representations thereof.

Common user needs that can be addressed by a robust API for measurements include, but are not limited to:

  • The need to keep track of the precision of measured values. A measurement value represented with a large number of significant figures can imply that the measurements themselves are more precise than the apparatus used to take the measurement can support.

  • The need to represent currency values. Often users will want to keep track of money values together with the currency in which those values are denominated.

  • The need to format measurements into string representations

Description

We propose creating a new Amount API, whose values will be immutable and have the following properties:

Note: ⚠️ All property/method names up for bikeshedding.

  • currency (String or undefined): The currency code with which number should be understood (with undefined indicating "none supplied")
  • unit (String or undefined): The unit of measurement with which number should be understood (with undefined indicating "none supplied")
  • significantDigits (Number): how many significant digits does this value contain? (Should be a positive integer)
  • fractionalDigits (Number): how many digits are required to fully represent the part of the fractional part of the underlying mathematical value. (Should be a non-negative integer.)

Precision

A big question is how we should handle precision. When constructing an Amount, both the significant digits and fractional digits are recorded.

Constructor

  • new Amount(value[, options]). Constructs an Amount with the mathematical value of value, and optional options, of which the following are supported (all being optional):
    • currency or unit (String): a marker for the measurement (cannot supply both)
    • fractionDigits: the number of fractional digits the mathematical value should have (can be less than, equal to, or greater than the actual number of fractional digits that the underlying mathematical value has when rendered as a decimal digit string)
    • significantDigits: the number of significant digits that the mathematical value should have (can be less than, equal to, or greater than the actual number of significant digits that the underlying mathematical value has when rendered as a decimal digit string)
    • roundingMode: one of the seven supported Intl rounding modes. This option is used when the fractionDigits and significantDigits options are provided and rounding is necessary to ensure that the value really does have the specified number of fraction/significant digits.

The object prototype would provide the following methods:

  • toString([ options ]): Returns a string representation of the Amount. By default, returns a digit string together with the unit/currency in square brackets (e.g., "1.23[kg]) if the Amount does have an amount; otherwise, just the bare numeric value. With options specified (not undefined), we consult its displayUnit property, looking for three possible String values: "auto", "never", and "always". With "auto" (the default), we do what was just described previously. With displayUnit "never", we will never show the unit, even if the Amount does have one; and with displayUnit: "always" we will always show the unit, using "1" as the unit for Amounts without a unit (the "unit unit").

  • toLocaleString(locale[, options]): Return a formatted string representation appropriate to the locale (e.g., "1,23 kg" in a locale that uses a comma as a fraction separator). The options are the same as those for toString() above.

  • with(options): Create a new Amount based on this one, together with additional options.

Examples

Let's construct an Amount, query its properties, and render it. First, we'll work with a bare number (no unit):

let a = new Amount("123.456");
a.fractionDigits; // 3
a.significantDigits; // 6
a.with({ fractionDigits: 4 }).toString(); // "123.4560"

Notice that "upgrading" the precision of an Amount appends trailing zeroes to the number.

Here's an example with units:

let a = new Amount("42.7", { unit: "kg" });
a.toString(); // "42.7[kg]"
a.toString({ numberOnly: true }); // "42.7"

Rounding

If one downgrades the precision of an Amount, rounding will occur. (Upgrading just adds trailing zeroes.)

let a = new Amount("123.456");
a.with({ significantDigits: 5 }).toString(); // "123.46"

By default, we use the round-ties-to-even rounding mode, which is used by IEEE 754 standard, and thus by Number and Decimal. One can specify a rounding mode:

let b = new Amount("123.456");
a.with({ significantDigits: 5, roundingMode: "truncate" }).toString(); // "123.45"

Units (including currency)

A core piece of functionality for the proposal is to support units (mile, kilogram, etc.) as well as currency (EUR, USD, etc.). An Amount need not have a unit/currency, and if it does, it has one or the other (not both). Example:

let a = new Amount("123.456", { unit: "kg" }); // 123.456 kilograms
let b = new Amount("42.55", { currency: "EUR" }); // 42.55 Euros

When serializing an Amount that has a unit/currency marker, units are automatically rendered in lowercase and currency always in uppercase.

Note that, currently, no meaning is specified for currencies or units. You can use "XYZ" as a currency or "keelogramz" as a unit. Calling toLocaleString() on an Amount with a unit not supported by Intl.NumberFormat will throw an Error.

Related but out-of-scope features

Amount is intended to be a small, straightforwardly implementable kernel of functionality for JavaScript programmers that could perhaps be expanded upon in a follow-on proposal if data warrants. Some features that one might imagine belonging to Amount are natural and understandable, but are currently out-of scope. Here are the features:

Mathematical operations

Below is a list of mathematical operations that one could consider supporting. However, to avoid confusion and ambiguity about the meaning of propagating precision in arithmetic operations, we do not intend to support mathematical operations. A natural source of data would be the CLDR data for both our unit names and the conversion constants are as in CLDR. One could conceive of operations such as:

  • raising an Amount to an exponent
  • multiply/divide an Amount by a scalar
  • Add/subtract two Amounts of the same dimension
  • multiply/divide an Amount by another Amount
  • Convert between scales (e.g., convert from grams to kilograms)

could be imagined, but are out-of-scope in this proposal. This proposal focuses on the numeric core that future proposals can build on.

Unit conversion

One might want to convert an Amount from one unit (e.g., miles) to another (e.g., kilometers). We envision that functionality to be potentially introduced as part of the Smart Units proposal. This implies that converting from unit to another is not supported, as well as converting amounts between scales (e.g., converting grams to kilograms). Our work here in this proposal is designed to provide a foundation for such ideas.

Derived units

Some units can derive other units, such as square meters and cubic yards (to mention only a couple!). Support for such units is currently out-of-scope for this proposal.

Compound units

Some units can be combined. In the US, it is common to express the heights of people in terms of feet and inches, rather than a non-integer number of feet or a "large" number of inches. For instance, one would say commonly express a height of 71 inches as "5 feet 11 inches" rather than "71 inches" or "5.92 feet". Thus, one would naturally want to support "foot-and-inch" as a compound unit, derivable from a measurement in terms of feet or inches. Likewise, combining units to express, say, velocity (miles per hour) or density (grams per cubic centimeter) also falls under this umbrella. Since this is closely related to unit conversion, we prefer to see this functionality in Smart Units.

Related/See also

  • Smart Units (mentioned several times as a natural follow-on proposal to this one)
  • Decimal for exact decimal arithmetic
  • Keep trailing zeroes to ensure that when Intl handles digit strings, it doesn't automatically strip trailing zeroes (e.g., silently normalize "1.20" to "1.2").

About

No description, website, or topics provided.

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Contributors 4

  •  
  •  
  •  
  •