diff --git a/docs/users_guide/framework_basics/quantity_arithmetics.md b/docs/users_guide/framework_basics/quantity_arithmetics.md index 4795baba..62a487cc 100644 --- a/docs/users_guide/framework_basics/quantity_arithmetics.md +++ b/docs/users_guide/framework_basics/quantity_arithmetics.md @@ -337,7 +337,7 @@ Among others, we can find there the following: - `exp()`, - `abs()`, - `epsilon()`, -- `fma()`, +- `fma()`, `fmod()`, - `isfinite()`, `isinf()`, `isnan()`, - `floor()`, `ceil()`, `round()`, - `inverse()`, diff --git a/src/core/include/mp-units/math.h b/src/core/include/mp-units/math.h index 2ce773be..ac9ecae9 100644 --- a/src/core/include/mp-units/math.h +++ b/src/core/include/mp-units/math.h @@ -198,6 +198,24 @@ template } +/** + * @brief Computes the floating-point remainder of the division operation x / y. + */ +template + requires requires(Rep1 v1, Rep2 v2) { + common_reference(R1, R2); + requires requires { fmod(v1, v2); } || requires { std::fmod(v1, v2); }; + } +[[nodiscard]] constexpr QuantityOf auto fmod(const quantity& x, + const quantity& y) noexcept +{ + constexpr auto ref = common_reference(R1, R2); + constexpr auto unit = get_unit(ref); + using std::fmod; + return quantity{fmod(x.numerical_value_in(unit), y.numerical_value_in(unit)), ref}; +} + + /** * @brief Returns the epsilon of the quantity * diff --git a/test/runtime/math_test.cpp b/test/runtime/math_test.cpp index 643f8378..3671ebf8 100644 --- a/test/runtime/math_test.cpp +++ b/test/runtime/math_test.cpp @@ -71,6 +71,24 @@ TEST_CASE("'fma()' on quantity changes the value and the dimension accordingly", REQUIRE(fma(isq::speed(10.0 * m / s), isq::time(2.0 * s), isq::height(42.0 * m)) == isq::length(62.0 * m)); } +TEST_CASE("fmod functions", "[math][fmod]") +{ + SECTION("fmod should work on the same quantities") + { + REQUIRE(fmod(4. * isq::length[km], 3. * isq::length[km]) == 1. * isq::length[km]); + REQUIRE(fmod(-9. * isq::length[km], 3. * isq::length[km]) == -0. * isq::length[km]); + REQUIRE(fmod(3 * isq::length[km], 2 * isq::length[km]) == 1 * isq::length[km]); + REQUIRE(fmod(4 * isq::length[km], 2.5f * isq::length[km]) == 1.5 * isq::length[km]); + } + SECTION("fmod should work with different units of the same dimension") + { + REQUIRE(fmod(4. * isq::length[km], 3000. * isq::length[m]) == 1000. * isq::length[m]); + REQUIRE(fmod(-9. * isq::length[km], 3000. * isq::length[m]) == -0. * isq::length[m]); + REQUIRE(fmod(3. * isq::length[km], 2000. * isq::length[m]) == 1000 * isq::length[m]); + REQUIRE(fmod(4 * isq::length[km], 2500 * isq::length[m]) == 1500 * isq::length[m]); + } +} + TEST_CASE("'isfinite()' accepts dimensioned arguments", "[math][isfinite]") { REQUIRE(isfinite(4.0 * isq::length[m])); } TEST_CASE("'isinf()' accepts dimensioned arguments", "[math][isinf]") { REQUIRE(!isinf(4.0 * isq::length[m])); } diff --git a/test/static/math_test.cpp b/test/static/math_test.cpp index d4f52037..1f4e10f3 100644 --- a/test/static/math_test.cpp +++ b/test/static/math_test.cpp @@ -57,6 +57,12 @@ static_assert(compare(fma(2.0 * one, 3.0 * m, 1.0 * m), 7.0 * m)); static_assert(compare(fma(2.0 * m, 3.0 * one, 1.0 * m), 7.0 * m)); static_assert(compare(fma(2 * m, 3.0f * m, 1.0 * m2), 7.0 * m2)); static_assert(compare(fma(isq::width(2.0 * m), 2.0 * one, isq::height(3.0 * m)), isq::length(7.0 * m))); +static_assert(compare(fmod(4.0 * km, 3.0 * km), 1.0 * km)); +static_assert(compare(fmod(-4.0 * km, 3.0 * km), -1.0 * km)); +static_assert(compare(fmod(9.0 * km, -3.0 * km), 0.0 * km)); +static_assert(compare(fmod(9.5 * km, -2000 * m), 1500.0 * m)); +static_assert(compare(fmod(3 * km, 2 * km), 1.0 * km)); +static_assert(compare(fmod(4 * km, 2.5f * km), 1.5 * km)); static_assert(compare(pow<0>(2 * m), 1 * one)); static_assert(compare(pow<1>(2 * m), 2 * m)); static_assert(compare(pow<2>(2 * m), 4 * pow<2>(m), 4 * m2));