Created
May 24, 2022 19:20
-
-
Save linusg/331993c1d3bc3bd6b04f73deca69013c to your computer and use it in GitHub Desktop.
bigint nonsense
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp | |
index c5da1b12ac..aca68bdbd7 100644 | |
--- a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp | |
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp | |
@@ -1093,6 +1093,69 @@ double apply_unsigned_rounding_mode(double x, double r1, double r2, Optional<Uns | |
return r2; | |
} | |
+// 13.29 ApplyUnsignedRoundingMode ( x, r1, r2, unsignedRoundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-applyunsignedroundingmode | |
+Crypto::SignedBigInteger apply_unsigned_rounding_mode(Crypto::SignedDivisionResult const& x, Crypto::SignedBigInteger r1, Crypto::SignedBigInteger r2, Optional<UnsignedRoundingMode> const& unsigned_rounding_mode) | |
+{ | |
+ // 1. If x is equal to r1, return r1. | |
+ if (x.quotient == r1 && x.remainder == "0"_bigint) | |
+ return r1; | |
+ | |
+ // 2. Assert: r1 < x < r2. | |
+ if (x.remainder.is_negative()) | |
+ VERIFY(r1 < x.quotient && x.remainder != "0"_bigint && x.quotient <= r2); | |
+ else | |
+ VERIFY(r1 <= x.quotient && x.remainder != "0"_bigint && x.quotient < r2); | |
+ | |
+ // 3. Assert: unsignedRoundingMode is not undefined. | |
+ VERIFY(unsigned_rounding_mode.has_value()); | |
+ | |
+ // 4. If unsignedRoundingMode is zero, return r1. | |
+ if (unsigned_rounding_mode == UnsignedRoundingMode::Zero) | |
+ return r1; | |
+ | |
+ // 5. If unsignedRoundingMode is infinity, return r2. | |
+ if (unsigned_rounding_mode == UnsignedRoundingMode::Infinity) | |
+ return r2; | |
+ | |
+ // 6. Let d1 be x – r1. | |
+ auto d1 = Crypto::SignedDivisionResult { x.quotient.minus(r1), x.remainder }; | |
+ | |
+ // 7. Let d2 be r2 – x. | |
+ auto d2 = Crypto::SignedDivisionResult { r2.minus(x.quotient), x.remainder }; | |
+ | |
+ // 8. If d1 < d2, return r1. | |
+ if (d1.quotient < d2.quotient || (d1.quotient == d2.quotient && d1.remainder < d2.remainder)) | |
+ return r1; | |
+ | |
+ // 9. If d2 < d1, return r2. | |
+ if (d2.quotient < d1.quotient || (d2.quotient == d1.quotient && d2.remainder < d1.remainder)) | |
+ return r2; | |
+ | |
+ // 10. Assert: d1 is equal to d2. | |
+ VERIFY(d1.quotient == d2.quotient && d1.remainder == d2.remainder); | |
+ | |
+ // 11. If unsignedRoundingMode is half-zero, return r1. | |
+ if (unsigned_rounding_mode == UnsignedRoundingMode::HalfZero) | |
+ return r1; | |
+ | |
+ // 12. If unsignedRoundingMode is half-infinity, return r2. | |
+ if (unsigned_rounding_mode == UnsignedRoundingMode::HalfInfinity) | |
+ return r2; | |
+ | |
+ // 13. Assert: unsignedRoundingMode is half-even. | |
+ VERIFY(unsigned_rounding_mode == UnsignedRoundingMode::HalfEven); | |
+ | |
+ // 14. Let cardinality be (r1 / (r2 – r1)) modulo 2. | |
+ auto cardinality = modulo(r1.divided_by(r2.minus(r1)).quotient, "2"_bigint); | |
+ | |
+ // 15. If cardinality is 0, return r1. | |
+ if (cardinality == "0"_bigint) | |
+ return r1; | |
+ | |
+ // 16. Return r2. | |
+ return r2; | |
+} | |
+ | |
// 13.30 RoundNumberToIncrement ( x, increment, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-roundnumbertoincrement | |
double round_number_to_increment(double x, u64 increment, StringView rounding_mode) | |
{ | |
@@ -1142,11 +1205,6 @@ double round_number_to_increment(double x, u64 increment, StringView rounding_mo | |
// 13.30 RoundNumberToIncrement ( x, increment, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-roundnumbertoincrement | |
Crypto::SignedBigInteger round_number_to_increment(Crypto::SignedBigInteger const& x, u64 increment, StringView rounding_mode) | |
{ | |
- // FIXME: I failed to make the BigInt variants of RoundNumberToIncrement/ApplyUnsignedRoundingMode work correctly, | |
- // evidently I can't math good enough. WIP code can be found here: https://gist.github.com/linusg/a4543e5f554a615fd1c1ee5dc50790e9 | |
- | |
- // 1. Assert: x and increment are mathematical values. | |
- // 2. Assert: roundingMode is "ceil", "floor", "trunc", or "halfExpand". | |
VERIFY(rounding_mode == "ceil"sv || rounding_mode == "floor"sv || rounding_mode == "trunc"sv || rounding_mode == "halfExpand"sv); | |
// OPTIMIZATION: If the increment is 1 the number is always rounded | |
@@ -1154,43 +1212,50 @@ Crypto::SignedBigInteger round_number_to_increment(Crypto::SignedBigInteger cons | |
return x; | |
auto increment_big_int = Crypto::UnsignedBigInteger::create_from(increment); | |
- // 3. Let quotient be x / increment. | |
+ | |
+ // 1. Let quotient be x / increment. | |
auto division_result = x.divided_by(increment_big_int); | |
// OPTIMIZATION: If there's no remainder the number is already rounded | |
if (division_result.remainder == Crypto::UnsignedBigInteger { 0 }) | |
return x; | |
- Crypto::SignedBigInteger rounded = move(division_result.quotient); | |
- // 4. If roundingMode is "ceil", then | |
- if (rounding_mode == "ceil"sv) { | |
- // a. Let rounded be -floor(-quotient). | |
- if (!division_result.remainder.is_negative()) | |
- rounded = rounded.plus(Crypto::UnsignedBigInteger { 1 }); | |
- } | |
- // 5. Else if roundingMode is "floor", then | |
- else if (rounding_mode == "floor"sv) { | |
- // a. Let rounded be floor(quotient). | |
- if (division_result.remainder.is_negative()) | |
- rounded = rounded.minus(Crypto::UnsignedBigInteger { 1 }); | |
- } | |
- // 6. Else if roundingMode is "trunc", then | |
- else if (rounding_mode == "trunc"sv) { | |
- // a. Let rounded be the RoundTowardsZero(quotient). | |
- // NOTE: This is a no-op | |
+ bool is_negative; | |
+ | |
+ // 2. If quotient < 0, then | |
+ if (division_result.quotient.is_negative() || (division_result.quotient == "0"_bigint && division_result.remainder.is_negative())) { | |
+ // a. Let isNegative be true. | |
+ is_negative = true; | |
+ | |
+ // b. Set quotient to -quotient. | |
+ division_result.quotient.negate(); | |
+ division_result.remainder.negate(); | |
} | |
- // 7. Else, | |
+ // 3. Else, | |
else { | |
- // a. Let rounded be ! RoundHalfAwayFromZero(quotient). | |
- if (division_result.remainder.multiplied_by(Crypto::UnsignedBigInteger { 2 }).unsigned_value() >= increment_big_int) { | |
- if (division_result.remainder.is_negative()) | |
- rounded = rounded.minus(Crypto::UnsignedBigInteger { 1 }); | |
- else | |
- rounded = rounded.plus(Crypto::UnsignedBigInteger { 1 }); | |
- } | |
+ // a. Let isNegative be false. | |
+ is_negative = false; | |
} | |
- // 8. Return rounded × increment. | |
+ // 4. Let unsignedRoundingMode be GetUnsignedRoundingMode(roundingMode, isNegative). | |
+ auto unsigned_rounding_mode = get_unsigned_rounding_mode(rounding_mode, is_negative); | |
+ | |
+ // 5. Let r1 be the largest integer such that r1 ≤ quotient. | |
+ auto r1 = division_result.quotient; | |
+ if (division_result.remainder.is_negative()) | |
+ r1 = r1.minus("1"_bigint); | |
+ | |
+ // 6. Let r2 be the smallest integer such that r2 > quotient. | |
+ auto r2 = r1.plus("1"_bigint); | |
+ | |
+ // 7. Let rounded be ApplyUnsignedRoundingMode(quotient, r1, r2, unsignedRoundingMode). | |
+ auto rounded = apply_unsigned_rounding_mode(division_result, r1, r2, unsigned_rounding_mode); | |
+ | |
+ // 8. If isNegative is true, set rounded to -rounded. | |
+ if (is_negative) | |
+ rounded.negate(); | |
+ | |
+ // 9. Return rounded × increment. | |
return rounded.multiplied_by(increment_big_int); | |
} | |
diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h | |
index 873cae9dd7..2da160f71f 100644 | |
--- a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h | |
+++ b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h | |
@@ -145,7 +145,7 @@ double sign(double); | |
double sign(Crypto::SignedBigInteger const&); | |
UnsignedRoundingMode get_unsigned_rounding_mode(StringView rounding_mode, bool is_negative); | |
double apply_unsigned_rounding_mode(double x, double r1, double r2, Optional<UnsignedRoundingMode> const&); | |
-Crypto::SignedBigInteger apply_unsigned_rounding_mode(Crypto::SignedBigInteger const&, Crypto::SignedBigInteger r1, Crypto::SignedBigInteger r2, Optional<UnsignedRoundingMode> const&); | |
+Crypto::SignedBigInteger apply_unsigned_rounding_mode(Crypto::SignedDivisionResult const&, Crypto::SignedBigInteger r1, Crypto::SignedBigInteger r2, Optional<UnsignedRoundingMode> const&); | |
double round_number_to_increment(double, u64 increment, StringView rounding_mode); | |
Crypto::SignedBigInteger round_number_to_increment(Crypto::SignedBigInteger const&, u64 increment, StringView rounding_mode); | |
ThrowCompletionOr<ISODateTime> parse_iso_date_time(GlobalObject&, ParseResult const& parse_result); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment