diff --git a/rtc_base/numerics/divide_round.h b/rtc_base/numerics/divide_round.h index 77bc486be8..90c67fca3c 100644 --- a/rtc_base/numerics/divide_round.h +++ b/rtc_base/numerics/divide_round.h @@ -34,13 +34,25 @@ template inline auto constexpr DivideRoundToNearest(Dividend dividend, Divisor divisor) { static_assert(std::is_integral(), ""); static_assert(std::is_integral(), ""); - RTC_DCHECK_GE(dividend, 0); RTC_DCHECK_GT(divisor, 0); + if (dividend < Dividend{0}) { + auto half_of_divisor = divisor / 2; + auto quotient = dividend / divisor; + auto remainder = dividend % divisor; + if (rtc::SafeGt(-remainder, half_of_divisor)) { + --quotient; + } + return quotient; + } + auto half_of_divisor = (divisor - 1) / 2; auto quotient = dividend / divisor; auto remainder = dividend % divisor; - return quotient + (rtc::SafeGt(remainder, half_of_divisor) ? 1 : 0); + if (rtc::SafeGt(remainder, half_of_divisor)) { + ++quotient; + } + return quotient; } } // namespace webrtc diff --git a/rtc_base/numerics/divide_round_unittest.cc b/rtc_base/numerics/divide_round_unittest.cc index 30ad4946c3..00548e1cb2 100644 --- a/rtc_base/numerics/divide_round_unittest.cc +++ b/rtc_base/numerics/divide_round_unittest.cc @@ -39,11 +39,18 @@ TEST(DivideRoundUpTest, WorksForMaxDividend) { TEST(DivideRoundToNearestTest, CanBeUsedAsConstexpr) { static constexpr int kOne = DivideRoundToNearest(5, 4); static constexpr int kTwo = DivideRoundToNearest(7, 4); - static_assert(kOne == 1, ""); - static_assert(kTwo == 2, ""); + static_assert(kOne == 1); + static_assert(kTwo == 2); + static_assert(DivideRoundToNearest(-5, 4) == -1); + static_assert(DivideRoundToNearest(-7, 4) == -2); } TEST(DivideRoundToNearestTest, DivideByOddNumber) { + EXPECT_EQ(DivideRoundToNearest(-5, 3), -2); + EXPECT_EQ(DivideRoundToNearest(-4, 3), -1); + EXPECT_EQ(DivideRoundToNearest(-3, 3), -1); + EXPECT_EQ(DivideRoundToNearest(-2, 3), -1); + EXPECT_EQ(DivideRoundToNearest(-1, 3), 0); EXPECT_EQ(DivideRoundToNearest(0, 3), 0); EXPECT_EQ(DivideRoundToNearest(1, 3), 0); EXPECT_EQ(DivideRoundToNearest(2, 3), 1); @@ -54,6 +61,13 @@ TEST(DivideRoundToNearestTest, DivideByOddNumber) { } TEST(DivideRoundToNearestTest, DivideByEvenNumberTieRoundsUp) { + EXPECT_EQ(DivideRoundToNearest(-7, 4), -2); + EXPECT_EQ(DivideRoundToNearest(-6, 4), -1); + EXPECT_EQ(DivideRoundToNearest(-5, 4), -1); + EXPECT_EQ(DivideRoundToNearest(-4, 4), -1); + EXPECT_EQ(DivideRoundToNearest(-3, 4), -1); + EXPECT_EQ(DivideRoundToNearest(-2, 4), 0); + EXPECT_EQ(DivideRoundToNearest(-1, 4), 0); EXPECT_EQ(DivideRoundToNearest(0, 4), 0); EXPECT_EQ(DivideRoundToNearest(1, 4), 0); EXPECT_EQ(DivideRoundToNearest(2, 4), 1); @@ -68,6 +82,9 @@ TEST(DivideRoundToNearestTest, LargeDivisor) { EXPECT_EQ(DivideRoundToNearest(std::numeric_limits::max() - 1, std::numeric_limits::max()), 1); + EXPECT_EQ(DivideRoundToNearest(std::numeric_limits::min(), + std::numeric_limits::max()), + -1); } TEST(DivideRoundToNearestTest, DivideSmallTypeByLargeType) { diff --git a/rtc_base/units/BUILD.gn b/rtc_base/units/BUILD.gn index e2ab873c9b..bbb87a009a 100644 --- a/rtc_base/units/BUILD.gn +++ b/rtc_base/units/BUILD.gn @@ -10,13 +10,14 @@ import("../../webrtc.gni") rtc_source_set("unit_base") { visibility = [ - "../../api/units:*", ":*", + "../../api/units:*", ] sources = [ "unit_base.h" ] deps = [ "../../rtc_base:checks", + "../../rtc_base:divide_round", "../../rtc_base:safe_conversions", ] } diff --git a/rtc_base/units/unit_base.h b/rtc_base/units/unit_base.h index e0a926fb8d..e8f8ec1956 100644 --- a/rtc_base/units/unit_base.h +++ b/rtc_base/units/unit_base.h @@ -18,6 +18,7 @@ #include #include "rtc_base/checks.h" +#include "rtc_base/numerics/divide_round.h" #include "rtc_base/numerics/safe_conversions.h" namespace webrtc { @@ -154,12 +155,7 @@ class UnitBase { constexpr typename std::enable_if::value, T>::type ToFraction() const { RTC_DCHECK(IsFinite()); - if (Unit_T::one_sided) { - return rtc::dchecked_cast( - DivRoundPositiveToNearest(value_, Denominator)); - } else { - return rtc::dchecked_cast(DivRoundToNearest(value_, Denominator)); - } + return rtc::dchecked_cast(DivideRoundToNearest(value_, Denominator)); } template constexpr typename std::enable_if::value, T>::type @@ -169,9 +165,7 @@ class UnitBase { template constexpr int64_t ToFractionOr(int64_t fallback_value) const { - return IsFinite() ? Unit_T::one_sided - ? DivRoundPositiveToNearest(value_, Denominator) - : DivRoundToNearest(value_, Denominator) + return IsFinite() ? DivideRoundToNearest(value_, Denominator) : fallback_value; } @@ -205,14 +199,6 @@ class UnitBase { constexpr const Unit_T& AsSubClassRef() const { return static_cast(*this); } - // Assumes that n >= 0 and d > 0. - static constexpr int64_t DivRoundPositiveToNearest(int64_t n, int64_t d) { - return (n + d / 2) / d; - } - // Assumes that d > 0. - static constexpr int64_t DivRoundToNearest(int64_t n, int64_t d) { - return (n + (n >= 0 ? d / 2 : -d / 2)) / d; - } int64_t value_; };