From 8b07453bce6744d3e979e0c667b349fe0e850bac Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Wed, 6 Oct 2021 22:25:28 +0100 Subject: [PATCH] LibJS: Add non-BigInt overload of round_number_to_increment() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unlike the spec we chose BigInt for the input and output types here as it was being used with ℝ(ns), ns being of type BigInt, in one place and a conversion to double would not be safe. Since in many places we'll have double input values, let's add a double overload of this function to avoid awkward conversions and expensive allocations. --- .../Runtime/Temporal/AbstractOperations.cpp | 42 +++++++++++++++++++ .../Runtime/Temporal/AbstractOperations.h | 1 + 2 files changed, 43 insertions(+) diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp index 41c4bcae24..e9483f1e81 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp +++ b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.cpp @@ -632,6 +632,48 @@ double constrain_to_range(double x, double minimum, double maximum) return min(max(x, minimum), maximum); } +// NOTE: We have two variants of this function, one using doubles and one using BigInts - most of the time +// doubles will be fine, but take care to choose the right one. The spec is not very clear about this, as +// it uses mathematical values which can be arbitrarily (but not infinitely) large. +// Incidentally V8's Temporal implementation does the same :^) + +// 13.32 RoundNumberToIncrement ( x, increment, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-roundnumbertoincrement +i64 round_number_to_increment(double x, u64 increment, StringView rounding_mode) +{ + // 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); + + // 3. Let quotient be x / increment. + auto quotient = x / (double)increment; + + double rounded; + + // 4. If roundingMode is "ceil", then + if (rounding_mode == "ceil"sv) { + // a. Let rounded be −floor(−quotient). + rounded = -floor(-quotient); + } + // 5. Else if roundingMode is "floor", then + else if (rounding_mode == "floor"sv) { + // a. Let rounded be floor(quotient). + rounded = floor(quotient); + } + // 6. Else if roundingMode is "trunc", then + else if (rounding_mode == "trunc"sv) { + // a. Let rounded be the integral part of quotient, removing any fractional digits. + rounded = trunc(quotient); + } + // 7. Else, + else { + // a. Let rounded be ! RoundHalfAwayFromZero(quotient). + rounded = round(quotient); + } + + // 8. Return rounded × increment. + return (i64)rounded * (i64)increment; +} + // 13.32 RoundNumberToIncrement ( x, increment, roundingMode ), https://tc39.es/proposal-temporal/#sec-temporal-roundnumbertoincrement BigInt* round_number_to_increment(GlobalObject& global_object, BigInt const& x, u64 increment, StringView rounding_mode) { diff --git a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h index d59eb448e4..f372a8d604 100644 --- a/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h +++ b/Userland/Libraries/LibJS/Runtime/Temporal/AbstractOperations.h @@ -102,6 +102,7 @@ Optional maximum_temporal_duration_rounding_increment(StringView unit); ThrowCompletionOr reject_temporal_calendar_type(GlobalObject&, Object&); String format_seconds_string_part(u8 second, u16 millisecond, u16 microsecond, u16 nanosecond, Variant const& precision); double constrain_to_range(double x, double minimum, double maximum); +i64 round_number_to_increment(double, u64 increment, StringView rounding_mode); BigInt* round_number_to_increment(GlobalObject&, BigInt const&, u64 increment, StringView rounding_mode); ThrowCompletionOr parse_iso_date_time(GlobalObject&, String const& iso_string); ThrowCompletionOr parse_temporal_instant_string(GlobalObject&, String const& iso_string);