1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-06-01 10:08:10 +00:00

LibJS: Update ToRawPrecision / ToRawFixed AO spec comments

This is a normative change in the Intl spec:
f0f66cf

There are two main changes here:
1. Converting BigInt/Number objects to mathematical values.
2. A change in how ToRawPrecision computes its exponent and significant
   digits.

For (1), we do not yet support BigInt number formatting, thus already
have coerced Number objects to a double. When BigInt is supported, the
number passed into these methods will likely still be a Value, thus can
be coereced then.

For (2), our implementation already returns the expected edge-case
results pointed out on the spec PR.
This commit is contained in:
Timothy Flynn 2022-01-02 12:45:52 -05:00 committed by Linus Groh
parent a3149c11e5
commit f16f3c4677

View file

@ -1129,11 +1129,14 @@ RawFormatResult to_raw_precision(double number, int min_precision, int max_preci
{
RawFormatResult result {};
// 1. Let p be maxPrecision.
// 1. Set x to (x).
// FIXME: Support BigInt number formatting.
// 2. Let p be maxPrecision.
int precision = max_precision;
int exponent = 0;
// 2. If x = 0, then
// 3. If x = 0, then
if (number == 0.0) {
// a. Let m be the String consisting of p occurrences of the character "0".
result.formatted_string = String::repeated('0', precision);
@ -1144,31 +1147,28 @@ RawFormatResult to_raw_precision(double number, int min_precision, int max_preci
// c. Let xFinal be 0.
result.rounded_number = 0;
}
// 3. Else,
// 4. Else,
else {
// FIXME: The result of these steps isn't entirely accurate for large values of 'p' (which
// defaults to 21, resulting in numbers on the order of 10^21). Either AK::format or
// our Number::toString AO (double_to_string in Value.cpp) will need to be improved
// to produce more accurate results.
// a. Let e be the base 10 logarithm of x rounded down to the nearest integer.
// a. Let e and n be integers such that 10^(p1) ≤ n < 10^p and for which n × 10^(ep+1) x is as close to zero as possible.
// If there are two such sets of e and n, pick the e and n for which n × 10^(ep+1) is larger.
exponent = log10floor(number);
double power = pow(10, exponent - precision + 1);
// b. Let n be an integer such that 10^(p1) ≤ n < 10^p and for which the exact mathematical value of n × 10^(ep+1) x
// is as close to zero as possible. If there is more than one such n, pick the one for which n × 10^(ep+1) is larger.
double n = round(number / power);
// c. Let m be the String consisting of the digits of the decimal representation of n (in order, with no leading zeroes).
// b. Let m be the String consisting of the digits of the decimal representation of n (in order, with no leading zeroes).
result.formatted_string = Value(n).to_string_without_side_effects();
// d. Let xFinal be n × 10^(ep+1).
// c. Let xFinal be n × 10^(ep+1).
result.rounded_number = n * power;
}
// 4. If e ≥ p1, then
// 5. If e ≥ p1, then
if (exponent >= (precision - 1)) {
// a. Let m be the string-concatenation of m and ep+1 occurrences of the character "0".
result.formatted_string = String::formatted(
@ -1179,7 +1179,7 @@ RawFormatResult to_raw_precision(double number, int min_precision, int max_preci
// b. Let int be e+1.
result.digits = exponent + 1;
}
// 5. Else if e ≥ 0, then
// 6. Else if e ≥ 0, then
else if (exponent >= 0) {
// a. Let m be the string-concatenation of the first e+1 characters of m, the character ".", and the remaining p(e+1) characters of m.
result.formatted_string = String::formatted(
@ -1190,7 +1190,7 @@ RawFormatResult to_raw_precision(double number, int min_precision, int max_preci
// b. Let int be e+1.
result.digits = exponent + 1;
}
// 6. Else,
// 7. Else,
else {
// a. Assert: e < 0.
// b. Let m be the string-concatenation of the String value "0.", (e+1) occurrences of the character "0", and m.
@ -1203,15 +1203,16 @@ RawFormatResult to_raw_precision(double number, int min_precision, int max_preci
result.digits = 1;
}
// 7. If m contains the character ".", and maxPrecision > minPrecision, then
// 8. If m contains the character ".", and maxPrecision > minPrecision, then
if (result.formatted_string.contains('.') && (max_precision > min_precision)) {
// a. Let cut be maxPrecision minPrecision.
int cut = max_precision - min_precision;
// Steps 8b-8c are implemented by cut_trailing_zeroes.
result.formatted_string = cut_trailing_zeroes(result.formatted_string, cut);
}
// 8. Return the Record { [[FormattedString]]: m, [[RoundedNumber]]: xFinal, [[IntegerDigitsCount]]: int }.
// 9. Return the Record { [[FormattedString]]: m, [[RoundedNumber]]: xFinal, [[IntegerDigitsCount]]: int }.
return result;
}
@ -1221,21 +1222,24 @@ RawFormatResult to_raw_fixed(double number, int min_fraction, int max_fraction)
{
RawFormatResult result {};
// 1. Let f be maxFraction.
// 1. Set x to (x).
// FIXME: Support BigInt number formatting.
// 2. Let f be maxFraction.
int fraction = max_fraction;
double power = pow(10, fraction);
// 2. Let n be an integer for which the exact mathematical value of n / 10^f x is as close to zero as possible. If there are two such n, pick the larger n.
// 3. Let n be an integer for which the exact mathematical value of n / 10^f x is as close to zero as possible. If there are two such n, pick the larger n.
double n = round(number * power);
// 3. Let xFinal be n / 10^f.
// 4. Let xFinal be n / 10^f.
result.rounded_number = n / power;
// 4. If n = 0, let m be the String "0". Otherwise, let m be the String consisting of the digits of the decimal representation of n (in order, with no leading zeroes).
// 5. If n = 0, let m be the String "0". Otherwise, let m be the String consisting of the digits of the decimal representation of n (in order, with no leading zeroes).
result.formatted_string = n == 0.0 ? String("0"sv) : Value(n).to_string_without_side_effects();
// 5. If f ≠ 0, then
// 6. If f ≠ 0, then
if (fraction != 0) {
// a. Let k be the number of characters in m.
auto decimals = result.formatted_string.length();
@ -1262,17 +1266,18 @@ RawFormatResult to_raw_fixed(double number, int min_fraction, int max_fraction)
// e. Let int be the number of characters in a.
result.digits = a.length();
}
// 6. Else, let int be the number of characters in m.
// 7. Else, let int be the number of characters in m.
else {
result.digits = result.formatted_string.length();
}
// 7. Let cut be maxFraction minFraction.
// 8. Let cut be maxFraction minFraction.
int cut = max_fraction - min_fraction;
// Steps 9-10 are implemented by cut_trailing_zeroes.
result.formatted_string = cut_trailing_zeroes(result.formatted_string, cut);
// 10. Return the Record { [[FormattedString]]: m, [[RoundedNumber]]: xFinal, [[IntegerDigitsCount]]: int }.
// 11. Return the Record { [[FormattedString]]: m, [[RoundedNumber]]: xFinal, [[IntegerDigitsCount]]: int }.
return result;
}