Bug 1566410 - Part 4: Add support for "notation" and "compactDisplay" options. r=jwalden

Differential Revision: https://phabricator.services.mozilla.com/D38331

--HG--
extra : moz-landing-system : lando
This commit is contained in:
André Bargull 2019-07-24 10:29:17 +00:00
parent 07fc204d24
commit 26d79a479c
10 changed files with 687 additions and 20 deletions

View File

@ -305,6 +305,23 @@ bool js::intl::NumberFormatterSkeleton::useGrouping(bool on) {
return on || appendToken(u"group-off");
}
bool js::intl::NumberFormatterSkeleton::notation(Notation style) {
switch (style) {
case Notation::Standard:
// Default, no additional tokens needed.
return true;
case Notation::Scientific:
return appendToken(u"scientific");
case Notation::Engineering:
return appendToken(u"engineering");
case Notation::CompactShort:
return appendToken(u"compact-short");
case Notation::CompactLong:
return appendToken(u"compact-long");
}
MOZ_CRASH("unexpected notation style");
}
bool js::intl::NumberFormatterSkeleton::signDisplay(SignDisplay display) {
switch (display) {
case SignDisplay::Auto:
@ -438,7 +455,15 @@ static UNumberFormatter* NewUNumberFormatter(
maximumSignificantDigits)) {
return nullptr;
}
} else {
}
bool hasMinimumFractionDigits;
if (!HasProperty(cx, internals, cx->names().minimumFractionDigits,
&hasMinimumFractionDigits)) {
return nullptr;
}
if (hasMinimumFractionDigits) {
if (!GetProperty(cx, internals, internals,
cx->names().minimumFractionDigits, &value)) {
return nullptr;
@ -474,6 +499,51 @@ static UNumberFormatter* NewUNumberFormatter(
return nullptr;
}
if (!GetProperty(cx, internals, internals, cx->names().notation, &value)) {
return nullptr;
}
{
JSLinearString* notation = value.toString()->ensureLinear(cx);
if (!notation) {
return nullptr;
}
using Notation = intl::NumberFormatterSkeleton::Notation;
Notation style;
if (StringEqualsAscii(notation, "standard")) {
style = Notation::Standard;
} else if (StringEqualsAscii(notation, "scientific")) {
style = Notation::Scientific;
} else if (StringEqualsAscii(notation, "engineering")) {
style = Notation::Engineering;
} else {
MOZ_ASSERT(StringEqualsAscii(notation, "compact"));
if (!GetProperty(cx, internals, internals, cx->names().compactDisplay,
&value)) {
return nullptr;
}
JSLinearString* compactDisplay = value.toString()->ensureLinear(cx);
if (!compactDisplay) {
return nullptr;
}
if (StringEqualsAscii(compactDisplay, "short")) {
style = Notation::CompactShort;
} else {
MOZ_ASSERT(StringEqualsAscii(compactDisplay, "long"));
style = Notation::CompactLong;
}
}
if (!skeleton.notation(style)) {
return nullptr;
}
}
if (!GetProperty(cx, internals, internals, cx->names().signDisplay, &value)) {
return nullptr;
}
@ -680,13 +750,13 @@ static FieldType GetFieldTypeForNumberField(UNumberFormatFields fieldName,
break;
case UNUM_EXPONENT_SYMBOL_FIELD:
return &JSAtomState::exponentSeparator;
case UNUM_EXPONENT_SIGN_FIELD:
return &JSAtomState::exponentMinusSign;
case UNUM_EXPONENT_FIELD:
MOZ_ASSERT_UNREACHABLE(
"exponent field unexpectedly found in "
"formatted number, even though UNUM_SCIENTIFIC "
"and scientific notation were never requested");
break;
return &JSAtomState::exponentInteger;
#ifndef U_HIDE_DRAFT_API
case UNUM_MEASURE_UNIT_FIELD:
@ -697,25 +767,19 @@ static FieldType GetFieldTypeForNumberField(UNumberFormatFields fieldName,
break;
case UNUM_COMPACT_FIELD:
MOZ_ASSERT_UNREACHABLE(
"unexpected compact field found, even though "
"we don't use any user-defined patterns that "
"would require a compact number notation");
break;
return &JSAtomState::compact;
#endif
#ifndef U_HIDE_DEPRECATED_API
case UNUM_FIELD_COUNT:
MOZ_ASSERT_UNREACHABLE(
"format field sentinel value returned by "
"iterator!");
"format field sentinel value returned by iterator!");
break;
#endif
}
MOZ_ASSERT_UNREACHABLE(
"unenumerated, undocumented format field returned "
"by iterator");
"unenumerated, undocumented format field returned by iterator");
return nullptr;
}

View File

@ -207,6 +207,21 @@ class MOZ_STACK_CLASS NumberFormatterSkeleton final {
*/
MOZ_MUST_USE bool useGrouping(bool on);
enum class Notation {
Standard,
Scientific,
Engineering,
CompactShort,
CompactLong
};
/**
* Set the notation style for this skeleton.
*
* https://github.com/unicode-org/icu/blob/master/docs/userguide/format_parse/numbers/skeletons.md#notation
*/
MOZ_MUST_USE bool notation(Notation style);
enum class SignDisplay { Auto, Never, Always, ExceptZero };
/**

View File

@ -62,6 +62,10 @@ function resolveNumberFormatInternals(lazyNumberFormatData) {
internalProps.currencyDisplay = lazyNumberFormatData.currencyDisplay;
}
// Intl.NumberFormat Unified API Proposal
var notation = lazyNumberFormatData.notation;
internalProps.notation = notation;
// Step 22.
internalProps.minimumIntegerDigits = lazyNumberFormatData.minimumIntegerDigits;
@ -81,6 +85,10 @@ function resolveNumberFormatInternals(lazyNumberFormatData) {
internalProps.maximumSignificantDigits = lazyNumberFormatData.maximumSignificantDigits;
}
// Intl.NumberFormat Unified API Proposal
if (notation === "compact")
internalProps.compactDisplay = lazyNumberFormatData.compactDisplay;
// Step 24.
internalProps.useGrouping = lazyNumberFormatData.useGrouping;
@ -133,7 +141,7 @@ function UnwrapNumberFormat(nf) {
*
* Spec: ECMAScript Internationalization API Specification, 11.1.1.
*/
function SetNumberFormatDigitOptions(lazyData, options, mnfdDefault, mxfdDefault) {
function SetNumberFormatDigitOptions(lazyData, options, mnfdDefault, mxfdDefault, notation) {
// We skip step 1 because we set the properties on a lazyData object.
// Steps 2-4.
@ -141,6 +149,7 @@ function SetNumberFormatDigitOptions(lazyData, options, mnfdDefault, mxfdDefault
assert(typeof mnfdDefault === "number", "SetNumberFormatDigitOptions");
assert(typeof mxfdDefault === "number", "SetNumberFormatDigitOptions");
assert(mnfdDefault <= mxfdDefault, "SetNumberFormatDigitOptions");
assert(typeof notation === "string", "SetNumberFormatDigitOptions");
// Steps 5-9.
const mnid = GetNumberOption(options, "minimumIntegerDigits", 1, 21, 1);
@ -189,7 +198,10 @@ function SetNumberFormatDigitOptions(lazyData, options, mnfdDefault, mxfdDefault
lazyData.maximumFractionDigits = mxfd;
}
// Step 13 (TODO: Not yet implemented).
// Step 13.
else if (notation === "compact") {
// Step 13.a (Omitted).
}
// Step 14.
else {
@ -277,6 +289,11 @@ function InitializeNumberFormat(numberFormat, thisValue, locales, options) {
//
// useGrouping: true / false,
//
// notation: "standard" / "scientific" / "engineering" / "compact",
//
// // optional, if notation is "compact"
// compactDisplay: "short" / "long",
//
// signDisplay: "auto" / "never" / "always" / "exceptZero",
// }
//
@ -334,7 +351,7 @@ function InitializeNumberFormat(numberFormat, thisValue, locales, options) {
if (style === "currency")
lazyNumberFormatData.currencyDisplay = currencyDisplay;
// Steps 20-22.
// Steps 20-21.
var mnfdDefault, mxfdDefault;
if (style === "currency") {
mnfdDefault = cDigits;
@ -343,7 +360,20 @@ function InitializeNumberFormat(numberFormat, thisValue, locales, options) {
mnfdDefault = 0;
mxfdDefault = style === "percent" ? 0 : 3;
}
SetNumberFormatDigitOptions(lazyNumberFormatData, options, mnfdDefault, mxfdDefault);
// Intl.NumberFormat Unified API Proposal
var notation = GetOption(options, "notation", "string",
["standard", "scientific", "engineering", "compact"], "standard");
lazyNumberFormatData.notation = notation;
// Step 22.
SetNumberFormatDigitOptions(lazyNumberFormatData, options, mnfdDefault, mxfdDefault, notation);
// Intl.NumberFormat Unified API Proposal
var compactDisplay = GetOption(options, "compactDisplay", "string",
["short", "long"], "short");
if (notation === "compact")
lazyNumberFormatData.compactDisplay = compactDisplay;
// Steps 23.
var useGrouping = GetOption(options, "useGrouping", "boolean", undefined, true);
@ -584,6 +614,12 @@ function Intl_NumberFormat_resolvedOptions() {
_DefineDataProperty(result, "useGrouping", internals.useGrouping);
var notation = internals.notation;
_DefineDataProperty(result, "notation", notation);
if (notation === "compact")
_DefineDataProperty(result, "compactDisplay", internals.compactDisplay);
_DefineDataProperty(result, "signDisplay", internals.signDisplay);
// Step 6.

View File

@ -161,7 +161,7 @@ function InitializePluralRules(pluralRules, locales, options) {
lazyPluralRulesData.type = type;
// Step 9.
SetNumberFormatDigitOptions(lazyPluralRulesData, options, 0, 3);
SetNumberFormatDigitOptions(lazyPluralRulesData, options, 0, 3, "standard");
// Step 15.
//

View File

@ -0,0 +1,134 @@
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
const {
Nan, Inf, Integer, MinusSign, PlusSign, Decimal, Fraction, Group, Literal,
Compact,
} = NumberFormatParts;
const testcases = [
{
locale: "en",
options: {
notation: "compact",
compactDisplay: "long",
},
values: [
{value: +0, string: "0", parts: [Integer("0")]},
{value: -0, string: "-0", parts: [MinusSign("-"), Integer("0")]},
{value: 0n, string: "0", parts: [Integer("0")]},
{value: 1, string: "1", parts: [Integer("1")]},
{value: 10, string: "10", parts: [Integer("10")]},
{value: 100, string: "100", parts: [Integer("100")]},
{value: 1000, string: "1 thousand", parts: [Integer("1"), Literal(" "), Compact("thousand")]},
{value: 10000, string: "10 thousand", parts: [Integer("10"), Literal(" "), Compact("thousand")]},
{value: 100000, string: "100 thousand", parts: [Integer("100"), Literal(" "), Compact("thousand")]},
{value: 1000000, string: "1 million", parts: [Integer("1"), Literal(" "), Compact("million")]},
{value: 10000000, string: "10 million", parts: [Integer("10"), Literal(" "), Compact("million")]},
{value: 100000000, string: "100 million", parts: [Integer("100"), Literal(" "), Compact("million")]},
{value: 1000000000, string: "1 billion", parts: [Integer("1"), Literal(" "), Compact("billion")]},
{value: 10000000000, string: "10 billion", parts: [Integer("10"), Literal(" "), Compact("billion")]},
{value: 100000000000, string: "100 billion", parts: [Integer("100"), Literal(" "), Compact("billion")]},
{value: 1000000000000, string: "1 trillion", parts: [Integer("1"), Literal(" "), Compact("trillion")]},
{value: 10000000000000, string: "10 trillion", parts: [Integer("10"), Literal(" "), Compact("trillion")]},
{value: 100000000000000, string: "100 trillion", parts: [Integer("100"), Literal(" "), Compact("trillion")]},
{value: 1000000000000000, string: "1000 trillion", parts: [Integer("1000"), Literal(" "), Compact("trillion")]},
{value: 10000000000000000, string: "10,000 trillion", parts: [Integer("10"), Group(","), Integer("000"), Literal(" "), Compact("trillion")]},
{value: 100000000000000000, string: "100,000 trillion", parts: [Integer("100"), Group(","), Integer("000"), Literal(" "), Compact("trillion")]},
{value: 1n, string: "1", parts: [Integer("1")]},
{value: 10n, string: "10", parts: [Integer("10")]},
{value: 100n, string: "100", parts: [Integer("100")]},
{value: 1000n, string: "1 thousand", parts: [Integer("1"), Literal(" "), Compact("thousand")]},
{value: 10000n, string: "10 thousand", parts: [Integer("10"), Literal(" "), Compact("thousand")]},
{value: 100000n, string: "100 thousand", parts: [Integer("100"), Literal(" "), Compact("thousand")]},
{value: 1000000n, string: "1 million", parts: [Integer("1"), Literal(" "), Compact("million")]},
{value: 10000000n, string: "10 million", parts: [Integer("10"), Literal(" "), Compact("million")]},
{value: 100000000n, string: "100 million", parts: [Integer("100"), Literal(" "), Compact("million")]},
{value: 1000000000n, string: "1 billion", parts: [Integer("1"), Literal(" "), Compact("billion")]},
{value: 10000000000n, string: "10 billion", parts: [Integer("10"), Literal(" "), Compact("billion")]},
{value: 100000000000n, string: "100 billion", parts: [Integer("100"), Literal(" "), Compact("billion")]},
{value: 1000000000000n, string: "1 trillion", parts: [Integer("1"), Literal(" "), Compact("trillion")]},
{value: 10000000000000n, string: "10 trillion", parts: [Integer("10"), Literal(" "), Compact("trillion")]},
{value: 100000000000000n, string: "100 trillion", parts: [Integer("100"), Literal(" "), Compact("trillion")]},
{value: 1000000000000000n, string: "1000 trillion", parts: [Integer("1000"), Literal(" "), Compact("trillion")]},
{value: 10000000000000000n, string: "10,000 trillion", parts: [Integer("10"), Group(","), Integer("000"), Literal(" "), Compact("trillion")]},
{value: 100000000000000000n, string: "100,000 trillion", parts: [Integer("100"), Group(","), Integer("000"), Literal(" "), Compact("trillion")]},
{value: 0.1, string: "0.1", parts: [Integer("0"), Decimal("."), Fraction("1")]},
{value: 0.01, string: "0.01", parts: [Integer("0"), Decimal("."), Fraction("01")]},
{value: 0.001, string: "0.001", parts: [Integer("0"), Decimal("."), Fraction("001")]},
{value: 0.0001, string: "0.0001", parts: [Integer("0"), Decimal("."), Fraction("0001")]},
{value: 0.00001, string: "0.00001", parts: [Integer("0"), Decimal("."), Fraction("00001")]},
{value: 0.000001, string: "0.000001", parts: [Integer("0"), Decimal("."), Fraction("000001")]},
{value: 0.0000001, string: "0.0000001", parts: [Integer("0"), Decimal("."), Fraction("0000001")]},
{value: 12, string: "12", parts: [Integer("12")]},
{value: 123, string: "123", parts: [Integer("123")]},
{value: 1234, string: "1.2 thousand", parts: [Integer("1"), Decimal("."), Fraction("2"), Literal(" "), Compact("thousand")]},
{value: 12345, string: "12 thousand", parts: [Integer("12"), Literal(" "), Compact("thousand")]},
{value: 123456, string: "123 thousand", parts: [Integer("123"), Literal(" "), Compact("thousand")]},
{value: 1234567, string: "1.2 million", parts: [Integer("1"), Decimal("."), Fraction("2"), Literal(" "), Compact("million")]},
{value: 12345678, string: "12 million", parts: [Integer("12"), Literal(" "), Compact("million")]},
{value: 123456789, string: "123 million", parts: [Integer("123"), Literal(" "), Compact("million")]},
{value: Infinity, string: "∞", parts: [Inf("∞")]},
{value: -Infinity, string: "-∞", parts: [MinusSign("-"), Inf("∞")]},
{value: NaN, string: "NaN", parts: [Nan("NaN")]},
{value: -NaN, string: "NaN", parts: [Nan("NaN")]},
],
},
// The "{compactName}" placeholder may have different plural forms.
{
locale: "de",
options: {
notation: "compact",
compactDisplay: "long",
},
values: [
{value: 1e6, string: "1 Million", parts: [Integer("1"), Literal(" "), Compact("Million")]},
{value: 2e6, string: "2 Millionen", parts: [Integer("2"), Literal(" "), Compact("Millionen")]},
{value: 1e9, string: "1 Milliarde", parts: [Integer("1"), Literal(" "), Compact("Milliarde")]},
{value: 2e9, string: "2 Milliarden", parts: [Integer("2"), Literal(" "), Compact("Milliarden")]},
],
},
// Digits are grouped in myriads (every 10,000) in Japanese.
{
locale: "ja",
options: {
notation: "compact",
compactDisplay: "long",
},
values: [
{value: 1, string: "1", parts: [Integer("1")]},
{value: 10, string: "10", parts: [Integer("10")]},
{value: 100, string: "100", parts: [Integer("100")]},
{value: 1000, string: "1000", parts: [Integer("1000")]},
{value: 10e3, string: "1\u4E07", parts: [Integer("1"), Compact("\u4E07")]},
{value: 100e3, string: "10\u4E07", parts: [Integer("10"), Compact("\u4E07")]},
{value: 1000e3, string: "100\u4E07", parts: [Integer("100"), Compact("\u4E07")]},
{value: 10000e3, string: "1000\u4E07", parts: [Integer("1000"), Compact("\u4E07")]},
{value: 10e7, string: "1\u5104", parts: [Integer("1"), Compact("\u5104")]},
{value: 100e7, string: "10\u5104", parts: [Integer("10"), Compact("\u5104")]},
{value: 1000e7, string: "100\u5104", parts: [Integer("100"), Compact("\u5104")]},
{value: 10000e7, string: "1000\u5104", parts: [Integer("1000"), Compact("\u5104")]},
{value: 10e11, string: "1\u5146", parts: [Integer("1"), Compact("\u5146")]},
{value: 100e11, string: "10\u5146", parts: [Integer("10"), Compact("\u5146")]},
{value: 1000e11, string: "100\u5146", parts: [Integer("100"), Compact("\u5146")]},
{value: 10000e11, string: "1000\u5146", parts: [Integer("1000"), Compact("\u5146")]},
{value: 100000e11, string: "10,000\u5146", parts: [Integer("10"), Group(","), Integer("000"), Compact("\u5146")]},
],
},
];
runNumberFormattingTestcases(testcases);
if (typeof reportCompare === "function")
reportCompare(true, true);

View File

@ -0,0 +1,136 @@
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
const {
Nan, Inf, Integer, MinusSign, PlusSign, Decimal, Fraction, Group, Literal,
Compact,
} = NumberFormatParts;
const testcases = [
{
locale: "en",
options: {
notation: "compact",
compactDisplay: "short",
},
values: [
{value: +0, string: "0", parts: [Integer("0")]},
{value: -0, string: "-0", parts: [MinusSign("-"), Integer("0")]},
{value: 0n, string: "0", parts: [Integer("0")]},
{value: 1, string: "1", parts: [Integer("1")]},
{value: 10, string: "10", parts: [Integer("10")]},
{value: 100, string: "100", parts: [Integer("100")]},
{value: 1000, string: "1K", parts: [Integer("1"), Compact("K")]},
{value: 10000, string: "10K", parts: [Integer("10"), Compact("K")]},
{value: 100000, string: "100K", parts: [Integer("100"), Compact("K")]},
{value: 1000000, string: "1M", parts: [Integer("1"), Compact("M")]},
{value: 10000000, string: "10M", parts: [Integer("10"), Compact("M")]},
{value: 100000000, string: "100M", parts: [Integer("100"), Compact("M")]},
{value: 1000000000, string: "1B", parts: [Integer("1"), Compact("B")]},
{value: 10000000000, string: "10B", parts: [Integer("10"), Compact("B")]},
{value: 100000000000, string: "100B", parts: [Integer("100"), Compact("B")]},
{value: 1000000000000, string: "1T", parts: [Integer("1"), Compact("T")]},
{value: 10000000000000, string: "10T", parts: [Integer("10"), Compact("T")]},
{value: 100000000000000, string: "100T", parts: [Integer("100"), Compact("T")]},
{value: 1000000000000000, string: "1000T", parts: [Integer("1000"), Compact("T")]},
{value: 10000000000000000, string: "10,000T", parts: [Integer("10"), Group(","), Integer("000"), Compact("T")]},
{value: 100000000000000000, string: "100,000T", parts: [Integer("100"), Group(","), Integer("000"), Compact("T")]},
{value: 1n, string: "1", parts: [Integer("1")]},
{value: 10n, string: "10", parts: [Integer("10")]},
{value: 100n, string: "100", parts: [Integer("100")]},
{value: 1000n, string: "1K", parts: [Integer("1"), Compact("K")]},
{value: 10000n, string: "10K", parts: [Integer("10"), Compact("K")]},
{value: 100000n, string: "100K", parts: [Integer("100"), Compact("K")]},
{value: 1000000n, string: "1M", parts: [Integer("1"), Compact("M")]},
{value: 10000000n, string: "10M", parts: [Integer("10"), Compact("M")]},
{value: 100000000n, string: "100M", parts: [Integer("100"), Compact("M")]},
{value: 1000000000n, string: "1B", parts: [Integer("1"), Compact("B")]},
{value: 10000000000n, string: "10B", parts: [Integer("10"), Compact("B")]},
{value: 100000000000n, string: "100B", parts: [Integer("100"), Compact("B")]},
{value: 1000000000000n, string: "1T", parts: [Integer("1"), Compact("T")]},
{value: 10000000000000n, string: "10T", parts: [Integer("10"), Compact("T")]},
{value: 100000000000000n, string: "100T", parts: [Integer("100"), Compact("T")]},
{value: 1000000000000000n, string: "1000T", parts: [Integer("1000"), Compact("T")]},
{value: 10000000000000000n, string: "10,000T", parts: [Integer("10"), Group(","), Integer("000"), Compact("T")]},
{value: 100000000000000000n, string: "100,000T", parts: [Integer("100"), Group(","), Integer("000"), Compact("T")]},
{value: 0.1, string: "0.1", parts: [Integer("0"), Decimal("."), Fraction("1")]},
{value: 0.01, string: "0.01", parts: [Integer("0"), Decimal("."), Fraction("01")]},
{value: 0.001, string: "0.001", parts: [Integer("0"), Decimal("."), Fraction("001")]},
{value: 0.0001, string: "0.0001", parts: [Integer("0"), Decimal("."), Fraction("0001")]},
{value: 0.00001, string: "0.00001", parts: [Integer("0"), Decimal("."), Fraction("00001")]},
{value: 0.000001, string: "0.000001", parts: [Integer("0"), Decimal("."), Fraction("000001")]},
{value: 0.0000001, string: "0.0000001", parts: [Integer("0"), Decimal("."), Fraction("0000001")]},
{value: 12, string: "12", parts: [Integer("12")]},
{value: 123, string: "123", parts: [Integer("123")]},
{value: 1234, string: "1.2K", parts: [Integer("1"), Decimal("."), Fraction("2"), Compact("K")]},
{value: 12345, string: "12K", parts: [Integer("12"), Compact("K")]},
{value: 123456, string: "123K", parts: [Integer("123"), Compact("K")]},
{value: 1234567, string: "1.2M", parts: [Integer("1"), Decimal("."), Fraction("2"), Compact("M")]},
{value: 12345678, string: "12M", parts: [Integer("12"), Compact("M")]},
{value: 123456789, string: "123M", parts: [Integer("123"), Compact("M")]},
{value: Infinity, string: "∞", parts: [Inf("∞")]},
{value: -Infinity, string: "-∞", parts: [MinusSign("-"), Inf("∞")]},
{value: NaN, string: "NaN", parts: [Nan("NaN")]},
{value: -NaN, string: "NaN", parts: [Nan("NaN")]},
],
},
// Compact symbol can consist of multiple characters, e.g. when it's an abbreviation.
{
locale: "de",
options: {
notation: "compact",
compactDisplay: "short",
},
values: [
{value: 1e6, string: "1\u00A0Mio.", parts: [Integer("1"), Literal("\u00A0"), Compact("Mio.")]},
{value: 1e9, string: "1\u00A0Mrd.", parts: [Integer("1"), Literal("\u00A0"), Compact("Mrd.")]},
{value: 1e12, string: "1\u00A0Bio.", parts: [Integer("1"), Literal("\u00A0"), Compact("Bio.")]},
// CLDR doesn't support "Billiarde" (Brd.), Trillion (Trill.), etc.
{value: 1e15, string: "1000\u00A0Bio.", parts: [Integer("1000"), Literal("\u00A0"), Compact("Bio.")]},
],
},
// Digits are grouped in myriads (every 10,000) in Chinese.
{
locale: "zh",
options: {
notation: "compact",
compactDisplay: "short",
},
values: [
{value: 1, string: "1", parts: [Integer("1")]},
{value: 10, string: "10", parts: [Integer("10")]},
{value: 100, string: "100", parts: [Integer("100")]},
{value: 1000, string: "1000", parts: [Integer("1000")]},
{value: 10e3, string: "1\u4E07", parts: [Integer("1"), Compact("\u4E07")]},
{value: 100e3, string: "10\u4E07", parts: [Integer("10"), Compact("\u4E07")]},
{value: 1000e3, string: "100\u4E07", parts: [Integer("100"), Compact("\u4E07")]},
{value: 10000e3, string: "1000\u4E07", parts: [Integer("1000"), Compact("\u4E07")]},
{value: 10e7, string: "1\u4EBF", parts: [Integer("1"), Compact("\u4EBF")]},
{value: 100e7, string: "10\u4EBF", parts: [Integer("10"), Compact("\u4EBF")]},
{value: 1000e7, string: "100\u4EBF", parts: [Integer("100"), Compact("\u4EBF")]},
{value: 10000e7, string: "1000\u4EBF", parts: [Integer("1000"), Compact("\u4EBF")]},
{value: 10e11, string: "1\u5146", parts: [Integer("1"), Compact("\u5146")]},
{value: 100e11, string: "10\u5146", parts: [Integer("10"), Compact("\u5146")]},
{value: 1000e11, string: "100\u5146", parts: [Integer("100"), Compact("\u5146")]},
{value: 10000e11, string: "1000\u5146", parts: [Integer("1000"), Compact("\u5146")]},
{value: 100000e11, string: "10,000\u5146", parts: [Integer("10"), Group(","), Integer("000"), Compact("\u5146")]},
],
},
];
runNumberFormattingTestcases(testcases);
if (typeof reportCompare === "function")
reportCompare(true, true);

View File

@ -0,0 +1,136 @@
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
const {
Nan, Inf, Integer, MinusSign, PlusSign, Decimal, Fraction, Group,
ExponentSeparator, ExponentInteger, ExponentMinusSign,
} = NumberFormatParts;
const testcases = [
{
locale: "en",
options: {
notation: "engineering",
},
values: [
{value: +0, string: "0E0", parts: [Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: -0, string: "-0E0", parts: [MinusSign("-"), Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: 0n, string: "0E0", parts: [Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: 1, string: "1E0", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: 10, string: "10E0", parts: [Integer("10"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: 100, string: "100E0", parts: [Integer("100"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: 1000, string: "1E3", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("3")]},
{value: 10000, string: "10E3", parts: [Integer("10"), ExponentSeparator("E"), ExponentInteger("3")]},
{value: 100000, string: "100E3", parts: [Integer("100"), ExponentSeparator("E"), ExponentInteger("3")]},
{value: 1000000, string: "1E6", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("6")]},
{value: 1n, string: "1E0", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: 10n, string: "10E0", parts: [Integer("10"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: 100n, string: "100E0", parts: [Integer("100"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: 1000n, string: "1E3", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("3")]},
{value: 10000n, string: "10E3", parts: [Integer("10"), ExponentSeparator("E"), ExponentInteger("3")]},
{value: 100000n, string: "100E3", parts: [Integer("100"), ExponentSeparator("E"), ExponentInteger("3")]},
{value: 1000000n, string: "1E6", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("6")]},
{value: 0.1, string: "100E-3", parts: [Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")]},
{value: 0.01, string: "10E-3", parts: [Integer("10"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")]},
{value: 0.001, string: "1E-3", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")]},
{value: 0.0001, string: "100E-6", parts: [Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("6")]},
{value: 0.00001, string: "10E-6", parts: [Integer("10"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("6")]},
{value: 0.000001, string: "1E-6", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("6")]},
{value: 0.0000001, string: "100E-9", parts: [Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("9")]},
{value: Infinity, string: "∞E0", parts: [Inf("∞"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: -Infinity, string: "-∞E0", parts: [MinusSign("-"), Inf("∞"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: NaN, string: "NaNE0", parts: [Nan("NaN"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: -NaN, string: "NaNE0", parts: [Nan("NaN"), ExponentSeparator("E"), ExponentInteger("0")]},
],
},
// Exponent modifications take place early, so while in the "standard" notation
// `Intl.NumberFormat("en", {maximumFractionDigits: 0}).format(0.1)` returns "0", for
// "engineering" notation the result string is not "0", but instead "100E-3".
{
locale: "en",
options: {
notation: "engineering",
maximumFractionDigits: 0,
},
values: [
{value: 0.1, string: "100E-3", parts: [
Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")
]},
],
},
{
locale: "en",
options: {
notation: "engineering",
minimumFractionDigits: 4,
},
values: [
{value: 10, string: "10.0000E0", parts: [
Integer("10"), Decimal("."), Fraction("0000"), ExponentSeparator("E"), ExponentInteger("0")
]},
{value: 0.1, string: "100.0000E-3", parts: [
Integer("100"), Decimal("."), Fraction("0000"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")
]},
],
},
{
locale: "en",
options: {
notation: "engineering",
minimumIntegerDigits: 4,
},
values: [
{value: 10, string: "0,010E0", parts: [
Integer("0"), Group(","), Integer("010"), ExponentSeparator("E"), ExponentInteger("0")
]},
{value: 0.1, string: "0,100E-3", parts: [
Integer("0"), Group(","), Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")
]},
],
},
{
locale: "en",
options: {
notation: "engineering",
minimumSignificantDigits: 4,
},
values: [
{value: 10, string: "10.00E0", parts: [
Integer("10"), Decimal("."), Fraction("00"), ExponentSeparator("E"), ExponentInteger("0")
]},
{value: 0.1, string: "100.0E-3", parts: [
Integer("100"), Decimal("."), Fraction("0"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")
]},
],
},
{
locale: "en",
options: {
notation: "engineering",
maximumSignificantDigits: 1,
},
values: [
{value: 12, string: "10E0", parts: [
Integer("10"), ExponentSeparator("E"), ExponentInteger("0")
]},
{value: 0.12, string: "100E-3", parts: [
Integer("100"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")
]},
],
},
];
runNumberFormattingTestcases(testcases);
if (typeof reportCompare === "function")
reportCompare(true, true);

View File

@ -0,0 +1,136 @@
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
const {
Nan, Inf, Integer, MinusSign, PlusSign, Decimal, Fraction, Group,
ExponentSeparator, ExponentInteger, ExponentMinusSign,
} = NumberFormatParts;
const testcases = [
{
locale: "en",
options: {
notation: "scientific",
},
values: [
{value: +0, string: "0E0", parts: [Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: -0, string: "-0E0", parts: [MinusSign("-"), Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: 0n, string: "0E0", parts: [Integer("0"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: 1, string: "1E0", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: 10, string: "1E1", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("1")]},
{value: 100, string: "1E2", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("2")]},
{value: 1000, string: "1E3", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("3")]},
{value: 10000, string: "1E4", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("4")]},
{value: 100000, string: "1E5", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("5")]},
{value: 1000000, string: "1E6", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("6")]},
{value: 1n, string: "1E0", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: 10n, string: "1E1", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("1")]},
{value: 100n, string: "1E2", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("2")]},
{value: 1000n, string: "1E3", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("3")]},
{value: 10000n, string: "1E4", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("4")]},
{value: 100000n, string: "1E5", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("5")]},
{value: 1000000n, string: "1E6", parts: [Integer("1"), ExponentSeparator("E"), ExponentInteger("6")]},
{value: 0.1, string: "1E-1", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1")]},
{value: 0.01, string: "1E-2", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("2")]},
{value: 0.001, string: "1E-3", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("3")]},
{value: 0.0001, string: "1E-4", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("4")]},
{value: 0.00001, string: "1E-5", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("5")]},
{value: 0.000001, string: "1E-6", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("6")]},
{value: 0.0000001, string: "1E-7", parts: [Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("7")]},
{value: Infinity, string: "∞E0", parts: [Inf("∞"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: -Infinity, string: "-∞E0", parts: [MinusSign("-"), Inf("∞"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: NaN, string: "NaNE0", parts: [Nan("NaN"), ExponentSeparator("E"), ExponentInteger("0")]},
{value: -NaN, string: "NaNE0", parts: [Nan("NaN"), ExponentSeparator("E"), ExponentInteger("0")]},
],
},
// Exponent modifications take place early, so while in the "standard" notation
// `Intl.NumberFormat("en", {maximumFractionDigits: 0}).format(0.1)` returns "0", for
// "scientific" notation the result string is not "0", but instead "1E-1".
{
locale: "en",
options: {
notation: "scientific",
maximumFractionDigits: 0,
},
values: [
{value: 0.1, string: "1E-1", parts: [
Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1")
]},
],
},
{
locale: "en",
options: {
notation: "scientific",
minimumFractionDigits: 4,
},
values: [
{value: 10, string: "1.0000E1", parts: [
Integer("1"), Decimal("."), Fraction("0000"), ExponentSeparator("E"), ExponentInteger("1")
]},
{value: 0.1, string: "1.0000E-1", parts: [
Integer("1"), Decimal("."), Fraction("0000"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1")
]},
],
},
{
locale: "en",
options: {
notation: "scientific",
minimumIntegerDigits: 4,
},
values: [
{value: 10, string: "0,001E1", parts: [
Integer("0"), Group(","), Integer("001"), ExponentSeparator("E"), ExponentInteger("1")
]},
{value: 0.1, string: "0,001E-1", parts: [
Integer("0"), Group(","), Integer("001"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1")
]},
],
},
{
locale: "en",
options: {
notation: "scientific",
minimumSignificantDigits: 4,
},
values: [
{value: 10, string: "1.000E1", parts: [
Integer("1"), Decimal("."), Fraction("000"), ExponentSeparator("E"), ExponentInteger("1")
]},
{value: 0.1, string: "1.000E-1", parts: [
Integer("1"), Decimal("."), Fraction("000"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1")
]},
],
},
{
locale: "en",
options: {
notation: "scientific",
maximumSignificantDigits: 1,
},
values: [
{value: 12, string: "1E1", parts: [
Integer("1"), ExponentSeparator("E"), ExponentInteger("1")
]},
{value: 0.12, string: "1E-1", parts: [
Integer("1"), ExponentSeparator("E"), ExponentMinusSign("-"), ExponentInteger("1")
]},
],
},
];
runNumberFormattingTestcases(testcases);
if (typeof reportCompare === "function")
reportCompare(true, true);

View File

@ -14,6 +14,10 @@ const NumberFormatParts = {
PercentSign: GenericPartCreator("percentSign"),
Currency: GenericPartCreator("currency"),
Literal: GenericPartCreator("literal"),
ExponentSeparator: GenericPartCreator("exponentSeparator"),
ExponentMinusSign: GenericPartCreator("exponentMinusSign"),
ExponentInteger: GenericPartCreator("exponentInteger"),
Compact: GenericPartCreator("compact"),
};
function assertParts(nf, x, expected) {

View File

@ -92,6 +92,8 @@
MACRO(CopyDataPropertiesUnfiltered, CopyDataPropertiesUnfiltered, \
"CopyDataPropertiesUnfiltered") \
MACRO(copyWithin, copyWithin, "copyWithin") \
MACRO(compact, compact, "compact") \
MACRO(compactDisplay, compactDisplay, "compactDisplay") \
MACRO(count, count, "count") \
MACRO(CreateResolvingFunctions, CreateResolvingFunctions, \
"CreateResolvingFunctions") \
@ -141,6 +143,9 @@
MACRO(escape, escape, "escape") \
MACRO(eval, eval, "eval") \
MACRO(exec, exec, "exec") \
MACRO(exponentInteger, exponentInteger, "exponentInteger") \
MACRO(exponentMinusSign, exponentMinusSign, "exponentMinusSign") \
MACRO(exponentSeparator, exponentSeparator, "exponentSeparator") \
MACRO(export, export_, "export") \
MACRO(extends, extends, "extends") \
MACRO(false, false_, "false") \
@ -299,6 +304,7 @@
MACRO(noFilename, noFilename, "noFilename") \
MACRO(nonincrementalReason, nonincrementalReason, "nonincrementalReason") \
MACRO(noStack, noStack, "noStack") \
MACRO(notation, notation, "notation") \
MACRO(notes, notes, "notes") \
MACRO(NumberFormat, NumberFormat, "NumberFormat") \
MACRO(numeric, numeric, "numeric") \