mirror of
https://github.com/RGBCube/serenity
synced 2025-07-13 16:07:34 +00:00
LibJS: Implement Intl.NumberFormat V3's [[RoundingPriority]] changes
This commit is contained in:
parent
9e50f25ac4
commit
bb9a44cd50
3 changed files with 87 additions and 16 deletions
|
@ -159,8 +159,6 @@ StringView NumberFormatBase::rounding_type_string() const
|
||||||
return "significantDigits"sv;
|
return "significantDigits"sv;
|
||||||
case RoundingType::FractionDigits:
|
case RoundingType::FractionDigits:
|
||||||
return "fractionDigits"sv;
|
return "fractionDigits"sv;
|
||||||
case RoundingType::CompactRounding:
|
|
||||||
return "compactRounding"sv;
|
|
||||||
case RoundingType::MorePrecision:
|
case RoundingType::MorePrecision:
|
||||||
return "morePrecision"sv;
|
return "morePrecision"sv;
|
||||||
case RoundingType::LessPrecision:
|
case RoundingType::LessPrecision:
|
||||||
|
@ -483,6 +481,7 @@ int currency_digits(StringView currency)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 15.5.3 FormatNumericToString ( intlObject, x ), https://tc39.es/ecma402/#sec-formatnumberstring
|
// 15.5.3 FormatNumericToString ( intlObject, x ), https://tc39.es/ecma402/#sec-formatnumberstring
|
||||||
|
// 1.1.5 FormatNumericToString ( intlObject, x ), https://tc39.es/proposal-intl-numberformat-v3/out/numberformat/proposed.html#sec-formatnumberstring
|
||||||
FormatResult format_numeric_to_string(GlobalObject& global_object, NumberFormatBase const& intl_object, Value number)
|
FormatResult format_numeric_to_string(GlobalObject& global_object, NumberFormatBase const& intl_object, Value number)
|
||||||
{
|
{
|
||||||
// 1. If ℝ(x) < 0 or x is -0𝔽, let isNegative be true; else let isNegative be false.
|
// 1. If ℝ(x) < 0 or x is -0𝔽, let isNegative be true; else let isNegative be false.
|
||||||
|
@ -510,20 +509,46 @@ FormatResult format_numeric_to_string(GlobalObject& global_object, NumberFormatB
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// 5. Else,
|
// 5. Else,
|
||||||
case NumberFormatBase::RoundingType::MorePrecision: // FIXME: Handle this case for NumberFormat V3.
|
case NumberFormatBase::RoundingType::MorePrecision:
|
||||||
case NumberFormatBase::RoundingType::LessPrecision: // FIXME: Handle this case for NumberFormat V3.
|
case NumberFormatBase::RoundingType::LessPrecision: {
|
||||||
case NumberFormatBase::RoundingType::CompactRounding:
|
// a. Let sResult be ToRawPrecision(x, intlObject.[[MinimumSignificantDigits]], intlObject.[[MaximumSignificantDigits]], unsignedRoundingMode).
|
||||||
// a. Assert: intlObject.[[RoundingType]] is compactRounding.
|
auto significant_result = to_raw_precision(global_object, number, intl_object.min_significant_digits(), intl_object.max_significant_digits());
|
||||||
// b. Let result be ToRawPrecision(x, 1, 2).
|
|
||||||
result = to_raw_precision(global_object, number, 1, 2);
|
|
||||||
|
|
||||||
// c. If result.[[IntegerDigitsCount]] > 1, then
|
// b. Let fResult be ToRawFixed(x, intlObject.[[MinimumFractionDigits]], intlObject.[[MaximumFractionDigits]], intlObject.[[RoundingIncrement]], unsignedRoundingMode).
|
||||||
if (result.digits > 1) {
|
auto fraction_result = to_raw_fixed(global_object, number, intl_object.min_fraction_digits(), intl_object.max_fraction_digits());
|
||||||
// i. Let result be ToRawFixed(x, 0, 0).
|
|
||||||
result = to_raw_fixed(global_object, number, 0, 0);
|
// c. If intlObj.[[RoundingType]] is morePrecision, then
|
||||||
|
if (intl_object.rounding_type() == NumberFormatBase::RoundingType::MorePrecision) {
|
||||||
|
// i. If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then
|
||||||
|
if (significant_result.rounding_magnitude <= fraction_result.rounding_magnitude) {
|
||||||
|
// 1. Let result be sResult.
|
||||||
|
result = move(significant_result);
|
||||||
|
}
|
||||||
|
// ii. Else,
|
||||||
|
else {
|
||||||
|
// 2. Let result be fResult.
|
||||||
|
result = move(fraction_result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// d. Else,
|
||||||
|
else {
|
||||||
|
// i. Assert: intlObj.[[RoundingType]] is lessPrecision.
|
||||||
|
VERIFY(intl_object.rounding_type() == NumberFormatBase::RoundingType::LessPrecision);
|
||||||
|
|
||||||
|
// ii. If sResult.[[RoundingMagnitude]] ≤ fResult.[[RoundingMagnitude]], then
|
||||||
|
if (significant_result.rounding_magnitude <= fraction_result.rounding_magnitude) {
|
||||||
|
// 1. Let result be fResult.
|
||||||
|
result = move(fraction_result);
|
||||||
|
}
|
||||||
|
// iii. Else,
|
||||||
|
else {
|
||||||
|
// 1. Let result be sResult.
|
||||||
|
result = move(significant_result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
|
@ -1102,7 +1127,8 @@ RawFormatResult to_raw_precision(GlobalObject& global_object, Value number, int
|
||||||
result.formatted_string = cut_trailing_zeroes(result.formatted_string, cut);
|
result.formatted_string = cut_trailing_zeroes(result.formatted_string, cut);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 9. Return the Record { [[FormattedString]]: m, [[RoundedNumber]]: xFinal, [[IntegerDigitsCount]]: int }.
|
// 9. Return the Record { [[FormattedString]]: m, [[RoundedNumber]]: xFinal, [[IntegerDigitsCount]]: int, [[RoundingMagnitude]]: e–p+1 }.
|
||||||
|
result.rounding_magnitude = exponent - precision + 1;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1164,7 +1190,8 @@ RawFormatResult to_raw_fixed(GlobalObject& global_object, Value number, int min_
|
||||||
// Steps 9-10 are implemented by cut_trailing_zeroes.
|
// Steps 9-10 are implemented by cut_trailing_zeroes.
|
||||||
result.formatted_string = cut_trailing_zeroes(result.formatted_string, cut);
|
result.formatted_string = cut_trailing_zeroes(result.formatted_string, cut);
|
||||||
|
|
||||||
// 11. Return the Record { [[FormattedString]]: m, [[RoundedNumber]]: xFinal, [[IntegerDigitsCount]]: int }.
|
// 11. Return the Record { [[FormattedString]]: m, [[RoundedNumber]]: xFinal, [[IntegerDigitsCount]]: int, [[RoundingMagnitude]]: –f }.
|
||||||
|
result.rounding_magnitude = -fraction;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ public:
|
||||||
Invalid,
|
Invalid,
|
||||||
SignificantDigits,
|
SignificantDigits,
|
||||||
FractionDigits,
|
FractionDigits,
|
||||||
CompactRounding, // FIXME: Remove this when corresponding AOs are updated for NumberFormat V3.
|
|
||||||
MorePrecision,
|
MorePrecision,
|
||||||
LessPrecision,
|
LessPrecision,
|
||||||
};
|
};
|
||||||
|
@ -255,7 +254,8 @@ struct FormatResult {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RawFormatResult : public FormatResult {
|
struct RawFormatResult : public FormatResult {
|
||||||
int digits { 0 }; // [[IntegerDigitsCount]]
|
int digits { 0 }; // [[IntegerDigitsCount]]
|
||||||
|
int rounding_magnitude { 0 }; // [[RoundingMagnitude]]
|
||||||
};
|
};
|
||||||
|
|
||||||
int currency_digits(StringView currency);
|
int currency_digits(StringView currency);
|
||||||
|
|
|
@ -523,6 +523,50 @@ describe("style=decimal", () => {
|
||||||
expect(plPl.format(123456)).toBe("123456");
|
expect(plPl.format(123456)).toBe("123456");
|
||||||
expect(plPl.format(1234567)).toBe("1234567");
|
expect(plPl.format(1234567)).toBe("1234567");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("roundingPriority=lessPrecision", () => {
|
||||||
|
const nf = (locale, minSignificant, maxSignificant, minFraction, maxFraction) => {
|
||||||
|
return new Intl.NumberFormat(locale, {
|
||||||
|
roundingPriority: "lessPrecision",
|
||||||
|
minimumSignificantDigits: minSignificant,
|
||||||
|
maximumSignificantDigits: maxSignificant,
|
||||||
|
minimumFractionDigits: minFraction,
|
||||||
|
maximumFractionDigits: maxFraction,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(nf("en", 2, undefined, 2, undefined).format(1)).toBe("1.00");
|
||||||
|
expect(nf("en", 3, undefined, 1, undefined).format(1)).toBe("1.0");
|
||||||
|
expect(nf("en", undefined, 2, undefined, 2).format(1.23)).toBe("1.2");
|
||||||
|
expect(nf("en", undefined, 3, undefined, 1).format(1.23)).toBe("1.2");
|
||||||
|
|
||||||
|
expect(nf("ar", 2, undefined, 2, undefined).format(1)).toBe("\u0661\u066b\u0660\u0660");
|
||||||
|
expect(nf("ar", 3, undefined, 1, undefined).format(1)).toBe("\u0661\u066b\u0660");
|
||||||
|
expect(nf("ar", undefined, 2, undefined, 2).format(1.23)).toBe("\u0661\u066b\u0662");
|
||||||
|
expect(nf("ar", undefined, 3, undefined, 1).format(1.23)).toBe("\u0661\u066b\u0662");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("roundingPriority=morePrecision", () => {
|
||||||
|
const nf = (locale, minSignificant, maxSignificant, minFraction, maxFraction) => {
|
||||||
|
return new Intl.NumberFormat(locale, {
|
||||||
|
roundingPriority: "morePrecision",
|
||||||
|
minimumSignificantDigits: minSignificant,
|
||||||
|
maximumSignificantDigits: maxSignificant,
|
||||||
|
minimumFractionDigits: minFraction,
|
||||||
|
maximumFractionDigits: maxFraction,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(nf("en", 2, undefined, 2, undefined).format(1)).toBe("1.0");
|
||||||
|
expect(nf("en", 3, undefined, 1, undefined).format(1)).toBe("1.00");
|
||||||
|
expect(nf("en", undefined, 2, undefined, 2).format(1.23)).toBe("1.23");
|
||||||
|
expect(nf("en", undefined, 3, undefined, 1).format(1.23)).toBe("1.23");
|
||||||
|
|
||||||
|
expect(nf("ar", 2, undefined, 2, undefined).format(1)).toBe("\u0661\u066b\u0660");
|
||||||
|
expect(nf("ar", 3, undefined, 1, undefined).format(1)).toBe("\u0661\u066b\u0660\u0660");
|
||||||
|
expect(nf("ar", undefined, 2, undefined, 2).format(1.23)).toBe("\u0661\u066b\u0662\u0663");
|
||||||
|
expect(nf("ar", undefined, 3, undefined, 1).format(1.23)).toBe("\u0661\u066b\u0662\u0663");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("style=percent", () => {
|
describe("style=percent", () => {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue