diff options
author | Timothy Flynn <trflynn89@pm.me> | 2022-01-02 12:45:52 -0500 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2022-01-02 20:07:03 +0100 |
commit | f16f3c46773d2ef38487e57ea840d8177064010e (patch) | |
tree | 786641b96b6916150b21da5be03f0e263ac8f76b /Userland/Libraries/LibJS/Runtime | |
parent | a3149c11e54ef4a63dea6a2fdbdca4a012abbc38 (diff) | |
download | serenity-f16f3c46773d2ef38487e57ea840d8177064010e.zip |
LibJS: Update ToRawPrecision / ToRawFixed AO spec comments
This is a normative change in the Intl spec:
https://github.com/tc39/ecma402/commit/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.
Diffstat (limited to 'Userland/Libraries/LibJS/Runtime')
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp | 51 |
1 files changed, 28 insertions, 23 deletions
diff --git a/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp b/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp index c8524d1b4c..4561b0d933 100644 --- a/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intl/NumberFormat.cpp @@ -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^(pā1) ā¤ n < 10^p and for which n Ć 10^(eāp+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^(eāp+1) is larger. exponent = log10floor(number); double power = pow(10, exponent - precision + 1); - - // b. Let n be an integer such that 10^(pā1) ā¤ n < 10^p and for which the exact mathematical value of n Ć 10^(eāp+1) ā x - // is as close to zero as possible. If there is more than one such n, pick the one for which n Ć 10^(eāp+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^(eāp+1). + // c. Let xFinal be n Ć 10^(eāp+1). result.rounded_number = n * power; } - // 4. If e ā„ pā1, then + // 5. If e ā„ pā1, then if (exponent >= (precision - 1)) { // a. Let m be the string-concatenation of m and eāp+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; } |