diff --git a/src/core/include/units/math.h b/src/core/include/units/math.h index 1c30574e..21b202d4 100644 --- a/src/core/include/units/math.h +++ b/src/core/include/units/math.h @@ -183,4 +183,40 @@ template } } +/** + * @brief Computes the smallest quantity with integer representation and unit type To with its number not less than q + * + * @tparam q Quantity being the base of the operation + * @return Quantity The rounded quantity with unit type To + */ +template +[[nodiscard]] constexpr quantity ceil(const quantity& q) noexcept + requires (!treat_as_floating_point) || + requires { ceil(q.number()); } || + requires { std::ceil(q.number()); } +{ + const auto handle_signed_results = [&](const T& res) { + if (res < q) + return res + T::one(); + return res; + }; + if constexpr(treat_as_floating_point) { + using std::ceil; + if constexpr(std::is_same_v) { + return quantity(ceil(q.number())); + } + else { + return handle_signed_results(quantity(ceil(quantity_cast(q).number()))); + } + } + else { + if constexpr(std::is_same_v) { + return q; + } + else { + return handle_signed_results(quantity_cast(q)); + } + } +} + } // namespace units diff --git a/test/unit_test/runtime/math_test.cpp b/test/unit_test/runtime/math_test.cpp index c360facb..56bc1d60 100644 --- a/test/unit_test/runtime/math_test.cpp +++ b/test/unit_test/runtime/math_test.cpp @@ -133,7 +133,7 @@ TEST_CASE("floor functions", "[floor]") SECTION ("floor 1.3 seconds with target unit second should be 1 second") { REQUIRE(floor(1.3_q_s) == 1_q_s); } - SECTION ("floor -1.3 seconds with target unit second should be -1 second") { + SECTION ("floor -1.3 seconds with target unit second should be -2 seconds") { REQUIRE(floor(-1.3_q_s) == -2_q_s); } SECTION ("floor 1001. milliseconds with target unit second should be 1 second") { @@ -150,6 +150,47 @@ TEST_CASE("floor functions", "[floor]") } } +TEST_CASE("ceil functions", "[ceil]") +{ + SECTION ("ceil 1 second with target unit second should be 1 second") { + REQUIRE(ceil(1_q_s) == 1_q_s); + } + SECTION ("ceil 1000 milliseconds with target unit second should be 1 second") { + REQUIRE(ceil(1000_q_ms) == 1_q_s); + } + SECTION ("ceil 1001 milliseconds with target unit second should be 2 seconds") { + REQUIRE(ceil(1001_q_ms) == 2_q_s); + } + SECTION ("ceil 1999 milliseconds with target unit second should be 2 seconds") { + REQUIRE(ceil(1999_q_ms) == 2_q_s); + } + SECTION ("ceil -1000 milliseconds with target unit second should be -1 second") { + REQUIRE(ceil(-1000_q_ms) == -1_q_s); + } + SECTION ("ceil -999 milliseconds with target unit second should be 0 seconds") { + REQUIRE(ceil(-999_q_ms) == 0_q_s); + } + SECTION ("ceil 1.3 seconds with target unit second should be 2 seconds") { + REQUIRE(ceil(1.3_q_s) == 2_q_s); + } + SECTION ("ceil -1.3 seconds with target unit second should be -1 second") { + REQUIRE(ceil(-1.3_q_s) == -1_q_s); + } + SECTION ("ceil 1001. milliseconds with target unit second should be 2 seconds") { + REQUIRE(ceil(1001._q_ms) == 2_q_s); + } + SECTION ("ceil 1999. milliseconds with target unit second should be 2 seconds") { + REQUIRE(ceil(1999._q_ms) == 2_q_s); + } +// TODO does not work, probably due to a bug in fpow10() see #311 +// SECTION ("ceil -1000. milliseconds with target unit second should be -1 second") { +// REQUIRE(ceil(-1000._q_ms) == -1_q_s); +// } + SECTION ("ceil -999. milliseconds with target unit second should be 0 seconds") { + REQUIRE(ceil(-999._q_ms) == 0_q_s); + } +} + TEMPLATE_TEST_CASE_SIG("pow() implementation exponentiates values to power N", "[math][pow][exp]", (std::intmax_t N, N), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20, 25) { diff --git a/test/unit_test/static/math_test.cpp b/test/unit_test/static/math_test.cpp index 9ec4eb39..e6ae7841 100644 --- a/test/unit_test/static/math_test.cpp +++ b/test/unit_test/static/math_test.cpp @@ -77,6 +77,26 @@ static_assert(floor(1001._q_ms) == 1_q_s); static_assert(floor(1999._q_ms) == 1_q_s); static_assert(floor(-1000._q_ms) == -1_q_s); static_assert(floor(-999._q_ms) == -1_q_s); + +// ceil +// integral types +static_assert(compare(1_q_s)), decltype(1_q_s)>); + +static_assert(compare(1000_q_ms)), decltype(1_q_s)>); +static_assert(compare(1001_q_ms)), decltype(2_q_s)>); +static_assert(compare(1999_q_ms)), decltype(2_q_s)>); +static_assert(compare(-1000_q_ms)), decltype(-1_q_s)>); +static_assert(compare(-999_q_ms)), decltype(0_q_s)>); + +// floating-point +static_assert(ceil(1.3_q_s) == 2_q_s); +static_assert(ceil(-1.3_q_s) == -1_q_s); + +static_assert(ceil(1000._q_ms) == 1_q_s); +static_assert(ceil(1001._q_ms) == 2_q_s); +static_assert(ceil(1999._q_ms) == 2_q_s); +static_assert(ceil(-1000._q_ms) == -1_q_s); +static_assert(ceil(-999._q_ms) == 0_q_s); #endif } // namespace