Skip to content

Instantly share code, notes, and snippets.

@linusg
Created May 24, 2022 16:41
Show Gist options
  • Save linusg/a4543e5f554a615fd1c1ee5dc50790e9 to your computer and use it in GitHub Desktop.
Save linusg/a4543e5f554a615fd1c1ee5dc50790e9 to your computer and use it in GitHub Desktop.
// 13.29 ApplyUnsignedRoundingMode ( x, r1, r2, unsignedRoundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-applyunsignedroundingmode
Crypto::SignedBigInteger apply_unsigned_rounding_mode(Crypto::SignedBigInteger 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 == r1)
return r1;
// 2. Assert: r1 < x < r2.
VERIFY(r1 < x && x < 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 = x.minus(r1);
// 7. Let d2 be r2 – x.
auto d2 = r2.minus(x);
// 8. If d1 < d2, return r1.
if (d1 < d2)
return r1;
// 9. If d2 < d1, return r2.
if (d2 < d1)
return r2;
// 10. Assert: d1 is equal to d2.
VERIFY(d1 == d2);
// 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
Crypto::SignedBigInteger round_number_to_increment(Crypto::SignedBigInteger const& x, u64 increment, StringView rounding_mode)
{
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
if (increment == 1)
return x;
auto increment_big_int = Crypto::UnsignedBigInteger::create_from(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;
bool is_negative;
// 2. If quotient < 0, then
if (division_result.quotient.is_negative()) {
// a. Let isNegative be true.
is_negative = true;
// b. Set quotient to -quotient.
division_result.quotient.negate();
}
// 3. Else,
else {
// a. Let isNegative be false.
is_negative = false;
}
// 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;
// 6. Let r2 be the smallest integer such that r2 > quotient.
auto r2 = division_result.quotient.plus("1"_bigint);
// 7. Let rounded be ApplyUnsignedRoundingMode(quotient, r1, r2, unsignedRoundingMode).
auto rounded = apply_unsigned_rounding_mode(division_result.quotient, 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);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment