Skip to content

Instantly share code, notes, and snippets.

@linusg
Created May 24, 2022 19:20
Show Gist options
  • Save linusg/331993c1d3bc3bd6b04f73deca69013c to your computer and use it in GitHub Desktop.
Save linusg/331993c1d3bc3bd6b04f73deca69013c to your computer and use it in GitHub Desktop.
bigint nonsense
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