AK: Simplify Duration::from_time_units and handle remainder overflow

Overflow could happen in the multiplication of the remainder seconds
back into time units. Instead, take a remainder of the time units from
the division and use that for the nanoseconds.
This commit is contained in:
Zaggy1024
2026-04-15 17:01:31 -05:00
committed by Gregory Bertilson
parent 586da4e610
commit affbe61da0
Notes: github-actions[bot] 2026-04-16 20:09:41 +00:00
2 changed files with 19 additions and 11 deletions

View File

@@ -73,18 +73,19 @@ Duration Duration::from_time_units(i64 time_units, u32 numerator, u32 denominato
VERIFY(numerator != 0);
VERIFY(denominator != 0);
auto seconds_checked = Checked<i64>(time_units);
seconds_checked.mul(numerator);
seconds_checked.div(denominator);
if (time_units < 0)
seconds_checked.sub(1);
if (seconds_checked.has_overflow())
if (Checked<i64>::multiplication_would_overflow(time_units, numerator))
return Duration(time_units >= 0 ? NumericLimits<i64>::max() : NumericLimits<i64>::min(), 0);
auto seconds = seconds_checked.value_unchecked();
auto seconds_in_time_units = seconds * denominator / numerator;
auto remainder_in_time_units = time_units - seconds_in_time_units;
auto nanoseconds = ((remainder_in_time_units * 1'000'000'000 * numerator) + (denominator / 2)) / denominator;
auto time_units_product = time_units * numerator;
auto seconds = time_units_product / denominator;
auto time_units_remainder = time_units_product % denominator;
if (time_units_remainder < 0) {
if (seconds == NumericLimits<i64>::min())
return Duration(NumericLimits<i64>::min(), 0);
seconds--;
time_units_remainder += denominator;
}
auto nanoseconds = ((time_units_remainder * 1'000'000'000) + (denominator / 2)) / denominator;
if (nanoseconds == 1'000'000'000) {
seconds++;
nanoseconds = 0;

View File

@@ -961,6 +961,13 @@ TEST_CASE(time_units)
EXPECT_EQ(Duration::from_time_units(-43776, 1, 14592), Duration::from_seconds(-3));
EXPECT_EQ(Duration::from_time_units(NumericLimits<i64>::min(), 1, 2), Duration::from_seconds(NumericLimits<i64>::min() / 2));
EXPECT_EQ(Duration::from_time_units(NumericLimits<i64>::min() + 1, 1, 2), Duration::from_seconds(NumericLimits<i64>::min() / 2) + Duration::from_milliseconds(500));
EXPECT_EQ(Duration::from_time_units(NumericLimits<i64>::min(), 1, 3), Duration::from_seconds(NumericLimits<i64>::min() / 3) - Duration::from_nanoseconds(666'666'667));
EXPECT_EQ(Duration::from_time_units(-1, 1, 2'000'000'000), Duration::zero());
EXPECT_EQ(Duration::from_time_units(-2, 1, 2'000'000'000), Duration::from_nanoseconds(-1));
EXPECT_EQ(Duration::from_milliseconds(999).to_time_units(1, 48'000), 47'952);
EXPECT_EQ(Duration::from_milliseconds(-12'500).to_time_units(1, 1'000), -12'500);
EXPECT_EQ(Duration::from_milliseconds(-12'500).to_time_units(1, 1'000), -12'500);