From 59204cf3a07d8a70d5c4974b86213a44d2dcd032 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Tue, 22 Sep 2026 10:06:13 +0100 Subject: [PATCH] Added more math wrappers. Move wrappers to clib namespace --- docs/maths/math.md | 208 ++++++++++++++++++++- include/etl/math.h | 238 +++++++++++++++++++++---- include/etl/platform.h | 13 ++ include/etl/private/to_string_helper.h | 4 +- test/test_math.cpp | 103 +++++++++++ 5 files changed, 527 insertions(+), 39 deletions(-) diff --git a/docs/maths/math.md b/docs/maths/math.md index 979610f1..25c23aea 100644 --- a/docs/maths/math.md +++ b/docs/maths/math.md @@ -49,10 +49,17 @@ ETL_CONSTEXPR bool is_exactly_equal(T value1, T value2) ## floor +Defined in the `etl::clib` namespace. + +--- + ```cpp ETL_NODISCARD float floor(float value) ETL_NOEXCEPT ``` +**Description** +A wrapper around `::floorf`. + **Returns** `::floorf(value)` @@ -62,6 +69,9 @@ float floor(float value) ETL_NOEXCEPT ETL_NODISCARD double floor(double value) ETL_NOEXCEPT ``` +**Description** +A wrapper around `::floor`. + **Returns** `::floor(value)` @@ -71,15 +81,28 @@ double floor(double value) ETL_NOEXCEPT ETL_NODISCARD long double floor(long double value) ETL_NOEXCEPT ``` +**Description** +A wrapper around `::floorl`. + **Returns** -`::floorl(value)` +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::floor` with casts to `double`. +otherwise +`::floorl(value)` ## ceil +Defined in the `etl::clib` namespace. + +--- + ```cpp ETL_NODISCARD float ceil(float value) ETL_NOEXCEPT ``` +**Description** +A wrapper around `::ceilf`. + **Returns** `::ceilf(value)` @@ -89,6 +112,9 @@ float ceil(float value) ETL_NOEXCEPT ETL_NODISCARD double ceil(double value) ETL_NOEXCEPT ``` +**Description** +A wrapper around `::ceil`. + **Returns** `::ceil(value)` @@ -98,5 +124,183 @@ double ceil(double value) ETL_NOEXCEPT ETL_NODISCARD long double ceil(long double value) ETL_NOEXCEPT ``` +**Description** +A wrapper around `::ceill`. + **Returns** -`::ceill(value)` +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::ceil` with casts to `double`. +otherwise +`::ceill(value)` + +## log10 + +Defined in the `etl::clib` namespace. + +--- + +```cpp +ETL_NODISCARD +float log10(float value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::log10f`. + +**Returns** +`::log10f(value)` + +--- + +```cpp +ETL_NODISCARD +double log10(double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::log10`. + +**Returns** +`::log10(value)` + +--- + +```cpp +ETL_NODISCARD +long double log10(long double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::log10l`. + +**Returns** +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::log10` with casts to `double`. +otherwise +`::log10l(value)` + +## round + +Defined in the `etl::clib` namespace. + +--- + +```cpp +ETL_NODISCARD +float round(float value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::roundf`. + +**Returns** +`::roundf(value)` + +--- + +```cpp +ETL_NODISCARD +double round(double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::round`. + +**Returns** +`::round(value)` + +--- + +```cpp +ETL_NODISCARD +long double round(long double value) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::roundl`. + +**Returns** +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::round` with casts to `double`. +otherwise +`::roundl(value)` + +## pow + +Defined in the `etl::clib` namespace. + +--- + +```cpp +ETL_NODISCARD +float pow(float base, float exponent) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::powf`. + +**Returns** +`::powf(base, exponent)` + +--- + +```cpp +ETL_NODISCARD +double pow(double base, double exponent) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::pow`. + +**Returns** +`::pow(base, exponent)` + +--- + +```cpp +ETL_NODISCARD +long double pow(long double base, long double exponent) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::powl`. + +**Returns** +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::pow` with casts to `double`. +otherwise +`::powl(base, intpart)` + +## modf + +Defined in the `etl::clib` namespace. + +--- + +```cpp +ETL_NODISCARD +float modf(float value, float* intpart) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::modff`. + +**Returns** +`::modff(value, intpart)` + +--- + +```cpp +ETL_NODISCARD +double modf(double value, double* intpart) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::modf`. + +**Returns** +`::modf(value, intpart)` + +--- + +```cpp +ETL_NODISCARD +long double modf(long double value, long double* intpart) ETL_NOEXCEPT +``` +**Description** +A wrapper around `::modfl`. + +**Returns** +If `ETL_NO_LONG_DOUBLE_MATH` is defined then returns +`::modf` with casts to `double`. +otherwise +`::modfl(value, intpart)` diff --git a/include/etl/math.h b/include/etl/math.h index a7c8190b..1856ef1b 100644 --- a/include/etl/math.h +++ b/include/etl/math.h @@ -132,47 +132,215 @@ namespace etl } #include "private/diagnostic_pop.h" - //*************************************************************************** - // floor - //*************************************************************************** - ETL_NODISCARD - inline float floor(float value) ETL_NOEXCEPT + namespace clib { - return ::floorf(value); - } + //*************************************************************************** + // floor + //*************************************************************************** + ETL_NODISCARD + inline float floor(float value) ETL_NOEXCEPT + { + return ::floorf(value); + } - ETL_NODISCARD - inline double floor(double value) ETL_NOEXCEPT - { - return ::floor(value); - } + ETL_NODISCARD + inline double floor(double value) ETL_NOEXCEPT + { + return ::floor(value); + } - ETL_NODISCARD - inline long double floor(long double value) ETL_NOEXCEPT - { - return ::floorl(value); - } +#if ETL_USING_LONG_DOUBLE_MATH + ETL_NODISCARD + inline long double floor(long double value) ETL_NOEXCEPT + { + return ::floorl(value); + } +#else + ETL_NODISCARD + inline long double floor(long double value) ETL_NOEXCEPT + { + return static_cast(::floor(static_cast(value))); + } +#endif - //*************************************************************************** - // ceil - //*************************************************************************** - ETL_NODISCARD - inline float ceil(float value) ETL_NOEXCEPT - { - return ::ceilf(value); - } + //*************************************************************************** + // ceil + //*************************************************************************** + ETL_NODISCARD + inline float ceil(float value) ETL_NOEXCEPT + { + return ::ceilf(value); + } - ETL_NODISCARD - inline double ceil(double value) ETL_NOEXCEPT - { - return ::ceil(value); - } + ETL_NODISCARD + inline double ceil(double value) ETL_NOEXCEPT + { + return ::ceil(value); + } - ETL_NODISCARD - inline long double ceil(long double value) ETL_NOEXCEPT - { - return ::ceill(value); - } +#if ETL_USING_LONG_DOUBLE_MATH + ETL_NODISCARD + inline long double ceil(long double value) ETL_NOEXCEPT + { + return ::ceill(value); + } +#else + ETL_NODISCARD + inline long double ceil(long double value) ETL_NOEXCEPT + { + return static_cast(::ceil(static_cast(value))); + } +#endif + + //*************************************************************************** + // log10 + //*************************************************************************** + ETL_NODISCARD + inline float log10(float value) ETL_NOEXCEPT + { + return ::log10f(value); + } + + ETL_NODISCARD + inline double log10(double value) ETL_NOEXCEPT + { + return ::log10(value); + } + +#if ETL_USING_LONG_DOUBLE_MATH + ETL_NODISCARD + inline long double log10(long double value) ETL_NOEXCEPT + { + return ::log10l(value); + } +#else + ETL_NODISCARD + inline long double log10(long double value) ETL_NOEXCEPT + { + return static_cast(::log10(static_cast(value))); + } +#endif + + //*************************************************************************** + // round + //*************************************************************************** + ETL_NODISCARD + inline float round(float value) ETL_NOEXCEPT + { + return ::roundf(value); + } + + ETL_NODISCARD + inline double round(double value) ETL_NOEXCEPT + { + return ::round(value); + } + +#if ETL_USING_LONG_DOUBLE_MATH + ETL_NODISCARD + inline long double round(long double value) ETL_NOEXCEPT + { + return ::roundl(value); + } +#else + ETL_NODISCARD + inline long double round(long double value) ETL_NOEXCEPT + { + return static_cast(::round(static_cast(value))); + } +#endif + + //*************************************************************************** + // fabs + //*************************************************************************** + ETL_NODISCARD + inline float fabs(float value) ETL_NOEXCEPT + { + return ::fabsf(value); + } + + ETL_NODISCARD + inline double fabs(double value) ETL_NOEXCEPT + { + return ::fabs(value); + } + +#if ETL_USING_LONG_DOUBLE_MATH + ETL_NODISCARD + inline long double fabs(long double value) ETL_NOEXCEPT + { + return ::fabsl(value); + } +#else + ETL_NODISCARD + inline long double fabs(long double value) ETL_NOEXCEPT + { + return static_cast(::fabs(static_cast(value))); + } +#endif + + //*************************************************************************** + // power + //*************************************************************************** + ETL_NODISCARD + inline float pow(float base, float exponent) ETL_NOEXCEPT + { + return ::powf(base, exponent); + } + + ETL_NODISCARD + inline double pow(double base, double exponent) ETL_NOEXCEPT + { + return ::pow(base, exponent); + } + +#if ETL_USING_LONG_DOUBLE_MATH + ETL_NODISCARD + inline long double pow(long double base, long double exponent) ETL_NOEXCEPT + { + return ::powl(base, exponent); + } +#else + ETL_NODISCARD + inline long double power(long double base, long double exponent) ETL_NOEXCEPT + { + return static_cast(::pow(static_cast(base), static_cast(exponent))); + } +#endif + + //*************************************************************************** + // modf + //*************************************************************************** + ETL_NODISCARD + inline float modf(float value, float* intpart) ETL_NOEXCEPT + { + return ::modff(value, intpart); + } + + ETL_NODISCARD + inline double modf(double value, double* intpart) ETL_NOEXCEPT + { + return ::modf(value, intpart); + } + +#if ETL_USING_LONG_DOUBLE_MATH + ETL_NODISCARD + inline long double modf(long double value, long double* intpart) ETL_NOEXCEPT + { + return ::modfl(value, intpart); + } +#else + ETL_NODISCARD + inline long double modf(long double value, long double* intpart) ETL_NOEXCEPT + { + double d_iptr; + double result = ::modf(static_cast(value), &d_iptr); + *intpart = static_cast(d_iptr); + + return static_cast(result); + } +#endif + } // namespace clib } // namespace etl #endif diff --git a/include/etl/platform.h b/include/etl/platform.h index 494b4981..3e936a82 100644 --- a/include/etl/platform.h +++ b/include/etl/platform.h @@ -187,6 +187,19 @@ SOFTWARE. #define ETL_NOT_USING_FORMAT_LONG_DOUBLE_MATH 0 #endif +//************************************* +// Helper macro for ETL_NO_LONG_DOUBLE_MATH. +// Define ETL_NO_LONG_DOUBLE_MATH if the toolchain does not provide +// long double math functions (log10l, floorl, powl, modfl, roundl). +// When defined, long double arguments are cast to double for math operations. +#if defined(ETL_NO_LONG_DOUBLE_MATH) + #define ETL_USING_LONG_DOUBLE_MATH 0 + #define ETL_NOT_USING_LONG_DOUBLE_MATH 1 +#else + #define ETL_USING_LONG_DOUBLE_MATH 1 + #define ETL_NOT_USING_LONG_DOUBLE_MATH 0 +#endif + //************************************* // Figure out things about the compiler, if haven't already done so in // etl_profile.h diff --git a/include/etl/private/to_string_helper.h b/include/etl/private/to_string_helper.h index ac9c8dbc..254116b4 100644 --- a/include/etl/private/to_string_helper.h +++ b/include/etl/private/to_string_helper.h @@ -347,7 +347,7 @@ namespace etl } // Find the integral part of the floating point - T f_integral = etl::floor(scaled); + T f_integral = etl::clib::floor(scaled); uworkspace_t integral = static_cast(f_integral); // Find the fractional part of the floating point. @@ -404,7 +404,7 @@ namespace etl } // Find the integral part of the floating point - T f_integral = etl::floor(etl::absolute(value)); + T f_integral = etl::clib::floor(etl::absolute(value)); uworkspace_t integral = static_cast(f_integral); // Find the fractional part of the floating point. diff --git a/test/test_math.cpp b/test/test_math.cpp index b28029e5..302e487f 100644 --- a/test/test_math.cpp +++ b/test/test_math.cpp @@ -130,5 +130,108 @@ namespace CHECK_TRUE(etl::is_exactly_equal(i1, i1)); CHECK_FALSE(etl::is_exactly_equal(i1, i2)); } + + //************************************************************************* + TEST(test_floor) + { + float f1 = 1.7f; + float f2 = -1.7f; + double d1 = 1.7; + double d2 = -1.7; + long double ld1 = 1.7L; + long double ld2 = -1.7L; + + CHECK_CLOSE(1.0f, etl::clib::floor(f1), 0.0001f); + CHECK_CLOSE(-2.0f, etl::clib::floor(f2), 0.0001f); + CHECK_CLOSE(1.0, etl::clib::floor(d1), 0.0001); + CHECK_CLOSE(-2.0, etl::clib::floor(d2), 0.0001); + CHECK_CLOSE(1.0L, etl::clib::floor(ld1), 0.0001L); + CHECK_CLOSE(-2.0L, etl::clib::floor(ld2), 0.0001L); + } + + //************************************************************************* + TEST(test_ceil) + { + float f1 = 1.2f; + float f2 = -1.2f; + double d1 = 1.2; + double d2 = -1.2; + long double ld1 = 1.2L; + long double ld2 = -1.2L; + + CHECK_CLOSE(2.0f, etl::clib::ceil(f1), 0.0001f); + CHECK_CLOSE(-1.0f, etl::clib::ceil(f2), 0.0001f); + CHECK_CLOSE(2.0, etl::clib::ceil(d1), 0.0001); + CHECK_CLOSE(-1.0, etl::clib::ceil(d2), 0.0001); + CHECK_CLOSE(2.0L, etl::clib::ceil(ld1), 0.0001L); + CHECK_CLOSE(-1.0L, etl::clib::ceil(ld2), 0.0001L); + } + + //************************************************************************* + TEST(test_log10) + { + float f1 = 100.0f; + double d1 = 1000.0; + long double ld1 = 10000.0L; + + CHECK_CLOSE(2.0f, etl::clib::log10(f1), 0.0001f); + CHECK_CLOSE(3.0, etl::clib::log10(d1), 0.0001); + CHECK_CLOSE(4.0L, etl::clib::log10(ld1), 0.0001L); + } + + //************************************************************************* + TEST(test_fabs) + { + float f1 = -1.5f; + float f2 = 1.5f; + double d1 = -2.5; + double d2 = 2.5; + long double ld1 = -3.5L; + long double ld2 = 3.5L; + + CHECK_CLOSE(1.5f, etl::clib::fabs(f1), 0.0001f); + CHECK_CLOSE(1.5f, etl::clib::fabs(f2), 0.0001f); + CHECK_CLOSE(2.5, etl::clib::fabs(d1), 0.0001); + CHECK_CLOSE(2.5, etl::clib::fabs(d2), 0.0001); + CHECK_CLOSE(3.5L, etl::clib::fabs(ld1), 0.0001L); + CHECK_CLOSE(3.5L, etl::clib::fabs(ld2), 0.0001L); + } + + //************************************************************************* + TEST(test_pow) + { + float fb = 2.0f; + float fe = 3.0f; + double db = 2.0; + double de = 10.0; + long double ldb = 2.0L; + long double lde = 4.0L; + + CHECK_CLOSE(8.0f, etl::clib::pow(fb, fe), 0.0001f); + CHECK_CLOSE(1024.0, etl::clib::pow(db, de), 0.0001); + CHECK_CLOSE(16.0L, etl::clib::pow(ldb, lde), 0.0001L); + } + + //************************************************************************* + TEST(test_modf) + { + float f1 = 3.75f; + float f_intpart = 0.0f; + double d1 = 3.75; + double d_intpart = 0.0; + long double ld1 = 3.75L; + long double ld_intpart = 0.0L; + + float f_frac = etl::clib::modf(f1, &f_intpart); + double d_frac = etl::clib::modf(d1, &d_intpart); + long double ld_frac = etl::clib::modf(ld1, &ld_intpart); + + CHECK_CLOSE(3.0f, f_intpart, 0.0001f); + CHECK_CLOSE(0.75f, f_frac, 0.0001f); + CHECK_CLOSE(3.0, d_intpart, 0.0001); + CHECK_CLOSE(0.75, d_frac, 0.0001); + CHECK_CLOSE((double)3.0L, (double)ld_intpart, 0.0001); + CHECK_CLOSE((double)0.75L, (double)ld_frac, 0.0001); + } } } // namespace