From 75c07eca5461b41128aac07e01bec564fe32e7bd Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Thu, 3 Sep 2026 14:38:51 +0200 Subject: [PATCH] Absolute and casting updates (#1583) --- docs/utilities/utility.md | 46 +++++++ include/etl/absolute.h | 19 ++- include/etl/utility.h | 24 ++++ test/test_math_functions.cpp | 104 +++++++++++++--- test/test_utility.cpp | 234 +++++++++++++++++++++++++++++++++++ 5 files changed, 400 insertions(+), 27 deletions(-) diff --git a/docs/utilities/utility.md b/docs/utilities/utility.md index c4f76c5f..48c0c3e3 100644 --- a/docs/utilities/utility.md +++ b/docs/utilities/utility.md @@ -605,3 +605,49 @@ struct nontype_t; ```cpp using FortyTwo = etl::nontype_t<42> ``` + +--- + +## to_unsigned + +```cpp +template +ETL_NODISCARD +ETL_CONSTEXPR14 +typename etl::make_unsigned::type + to_unsigned(T value) ETL_NOEXCEPT +``` + +**Description** +Casts `value` to the unsigned type. + +**Example** + +```cpp +int8_t a = -50; +auto b = etl::to_unsigned(a); +``` +`b` is of type `uint8_t` and contains the value `206`. + +--- + +## to_signed + +```cpp +template +ETL_NODISCARD +ETL_CONSTEXPR14 +typename etl::make_signed::type + to_signed(T value) ETL_NOEXCEPT +``` + +**Description** +Casts `value` to the signed type. + +**Example** + +```cpp +uint8_t a = 206; +auto b = etl::to_signed(a); +``` +`b` is of type `int8_t` and contains the value `-50`. diff --git a/include/etl/absolute.h b/include/etl/absolute.h index bfa68642..14988ffa 100644 --- a/include/etl/absolute.h +++ b/include/etl/absolute.h @@ -58,14 +58,13 @@ namespace etl // For signed types. //*************************************************************************** template - ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if< etl::is_signed::value && etl::is_integral::value, T>::type absolute(T value) + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value && etl::is_integral::value, T>::type absolute(T value) { return (value == etl::integral_limits::min) ? etl::private_absolute::signed_min_error() : static_cast((value < T(0)) ? -value : value); } template - ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if< etl::is_signed::value && !etl::is_integral::value, T>::type absolute(T value) - ETL_NOEXCEPT + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value && !etl::is_integral::value, T>::type absolute(T value) ETL_NOEXCEPT { return (value < T(0)) ? -value : value; } @@ -84,14 +83,13 @@ namespace etl // Returns the result as the unsigned type. //*************************************************************************** template - ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value, typename etl::make_unsigned::type>::type absolute_unsigned(T value) - ETL_NOEXCEPT + ETL_NODISCARD + ETL_CONSTEXPR typename etl::enable_if::value && etl::is_integral::value, typename etl::make_unsigned::type>::type + absolute_unsigned(T value) ETL_NOEXCEPT { typedef typename etl::make_unsigned::type TReturn; - return (value == etl::integral_limits::min) ? (etl::integral_limits::max / 2U) + 1U - : (value < T(0)) ? TReturn(-value) - : TReturn(value); + return (value < T(0)) ? 0U - static_cast(value) : static_cast(value); } //*************************************************************************** @@ -99,9 +97,10 @@ namespace etl // Returns the result as the unsigned type. //*************************************************************************** template - ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value, T>::type absolute_unsigned(T value) ETL_NOEXCEPT + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value && etl::is_integral::value, T>::type absolute_unsigned(T value) + ETL_NOEXCEPT { - return etl::absolute(value); + return value; } } // namespace etl diff --git a/include/etl/utility.h b/include/etl/utility.h index f11ccca8..6d93aa1f 100644 --- a/include/etl/utility.h +++ b/include/etl/utility.h @@ -1384,6 +1384,30 @@ namespace etl static constexpr T value = Value; }; #endif + + //*************************************************************************** + /// Returns 'value' cast to an unsigned integer. + ///\param value The integer to cast. + ///\return The unsigned integer. + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value, typename etl::make_unsigned::type>::type to_unsigned(T value) + ETL_NOEXCEPT + { + return static_cast::type>(value); + } + + //*************************************************************************** + /// Returns 'value' cast to a signed integer. + ///\param value The integer to cast. + ///\return The signed integer. + //*************************************************************************** + template + ETL_NODISCARD ETL_CONSTEXPR typename etl::enable_if::value, typename etl::make_signed::type>::type to_signed(T value) + ETL_NOEXCEPT + { + return static_cast::type>(value); + } } // namespace etl #endif diff --git a/test/test_math_functions.cpp b/test/test_math_functions.cpp index 3c672e42..05171bb0 100644 --- a/test/test_math_functions.cpp +++ b/test/test_math_functions.cpp @@ -556,7 +556,28 @@ namespace } //************************************************************************* - TEST(test_absolute_unsigned) + TEST(test_absolute_unsigned_int8_t) + { + // Edge cases for int8_t : min is -128, which has no positive int8_t + // counterpart, but is representable in uint8_t (128). + CHECK_EQUAL(uint8_t(0), etl::absolute_unsigned(int8_t(0))); + CHECK_EQUAL(uint8_t(127), etl::absolute_unsigned(int8_t(127))); + CHECK_EQUAL(uint8_t(127), etl::absolute_unsigned(int8_t(-127))); + CHECK_EQUAL(uint8_t(128), etl::absolute_unsigned(int8_t(-128))); // INT8_MIN edge case + + CHECK_EQUAL(uint8_t(0), etl::absolute_unsigned(uint8_t(0))); + CHECK_EQUAL(uint8_t(255), etl::absolute_unsigned(uint8_t(255))); + } + + //************************************************************************* + TEST(test_absolute_unsigned_uint8_t) + { + CHECK_EQUAL(uint8_t(0), etl::absolute_unsigned(uint8_t(0))); + CHECK_EQUAL(uint8_t(255), etl::absolute_unsigned(uint8_t(255))); + } + + //************************************************************************* + TEST(test_absolute_unsigned_int16_t) { CHECK_EQUAL(uint16_t(0), etl::absolute_unsigned(int16_t(0))); CHECK_EQUAL(uint16_t(32767), etl::absolute_unsigned(int16_t(32767))); @@ -568,23 +589,72 @@ namespace } //************************************************************************* - TEST(test_absolute_unsigned_constexpr) + TEST(test_absolute_unsigned_uint16_t) { - constexpr uint16_t absolute1 = etl::absolute(int16_t(0)); - constexpr uint16_t absolute2 = etl::absolute(int16_t(32767)); - constexpr uint16_t absolute3 = etl::absolute(int16_t(-32767)); - // constexpr uint16_t absolute4 = etl::absolute(int16_t(-32768)); // - // Compile error - - CHECK_EQUAL(uint16_t(0), absolute1); - CHECK_EQUAL(uint16_t(32767), absolute2); - CHECK_EQUAL(uint16_t(32767), absolute3); - - constexpr uint16_t absolute5 = etl::absolute(uint16_t(0)); - constexpr uint16_t absolute6 = etl::absolute(uint16_t(65535)); - - CHECK_EQUAL(uint16_t(0), absolute5); - CHECK_EQUAL(uint16_t(65535), absolute6); + CHECK_EQUAL(uint16_t(0), etl::absolute_unsigned(uint16_t(0))); + CHECK_EQUAL(uint16_t(65535), etl::absolute_unsigned(uint16_t(65535))); } + + //************************************************************************* + TEST(test_absolute_unsigned_int32_t) + { + CHECK_EQUAL(uint32_t(0), etl::absolute_unsigned(int32_t(0))); + CHECK_EQUAL(uint32_t(2147483647), etl::absolute_unsigned(int32_t(2147483647))); + CHECK_EQUAL(uint32_t(2147483647), etl::absolute_unsigned(int32_t(-2147483647))); + CHECK_EQUAL(uint32_t(2147483648u), etl::absolute_unsigned(int32_t(-2147483647 - 1))); // INT32_MIN edge case + + CHECK_EQUAL(uint32_t(0), etl::absolute_unsigned(uint32_t(0))); + CHECK_EQUAL(uint32_t(4294967295u), etl::absolute_unsigned(uint32_t(4294967295u))); + } + + //************************************************************************* + TEST(test_absolute_unsigned_uint32_t) + { + CHECK_EQUAL(uint32_t(0), etl::absolute_unsigned(uint32_t(0))); + CHECK_EQUAL(uint32_t(4294967295u), etl::absolute_unsigned(uint32_t(4294967295u))); + } + + //************************************************************************* + TEST(test_absolute_unsigned_int64_t) + { + CHECK_EQUAL(uint64_t(0), etl::absolute_unsigned(int64_t(0))); + CHECK_EQUAL(uint64_t(9223372036854775807LL), etl::absolute_unsigned(int64_t(9223372036854775807LL))); + CHECK_EQUAL(uint64_t(9223372036854775807LL), etl::absolute_unsigned(int64_t(-9223372036854775807LL))); + CHECK_EQUAL(uint64_t(9223372036854775808ULL), etl::absolute_unsigned(int64_t(-9223372036854775807LL - 1))); // INT64_MIN edge case + + CHECK_EQUAL(uint64_t(0), etl::absolute_unsigned(uint64_t(0))); + CHECK_EQUAL(uint64_t(18446744073709551615ULL), etl::absolute_unsigned(uint64_t(18446744073709551615ULL))); + } + + //************************************************************************* + TEST(test_absolute_unsigned_uint64_t) + { + CHECK_EQUAL(uint64_t(0), etl::absolute_unsigned(uint64_t(0))); + CHECK_EQUAL(uint64_t(18446744073709551615ULL), etl::absolute_unsigned(uint64_t(18446744073709551615ULL))); + } + +#if ETL_USING_CPP14 + //************************************************************************* + TEST(test_absolute_unsigned_constexpr14) + { + // Unlike etl::absolute, absolute_unsigned can represent INT_MIN safely, + // so this should be usable at compile time even for the minimum value. + constexpr uint16_t r1 = etl::absolute_unsigned(int16_t(0)); + constexpr uint16_t r2 = etl::absolute_unsigned(int16_t(32767)); + constexpr uint16_t r3 = etl::absolute_unsigned(int16_t(-32767)); + constexpr uint16_t r4 = etl::absolute_unsigned(int16_t(-32768)); // No compile error, unlike etl::absolute + + CHECK_EQUAL(uint16_t(0), r1); + CHECK_EQUAL(uint16_t(32767), r2); + CHECK_EQUAL(uint16_t(32767), r3); + CHECK_EQUAL(uint16_t(32768), r4); + + constexpr uint16_t r5 = etl::absolute_unsigned(uint16_t(0)); + constexpr uint16_t r6 = etl::absolute_unsigned(uint16_t(65535)); + + CHECK_EQUAL(uint16_t(0), r5); + CHECK_EQUAL(uint16_t(65535), r6); + } +#endif } } // namespace diff --git a/test/test_utility.cpp b/test/test_utility.cpp index 2093a355..124cff1f 100644 --- a/test/test_utility.cpp +++ b/test/test_utility.cpp @@ -1299,5 +1299,239 @@ namespace CHECK(true); } #endif + + //************************************************************************* + TEST(test_to_unsigned_from_int8_t) + { + auto umin = etl::to_unsigned(etl::integral_limits::min); + auto umax = etl::to_unsigned(etl::integral_limits::max); + auto uzero = etl::to_unsigned(int8_t(0)); + auto uplus = etl::to_unsigned(int8_t(50)); + auto uminus = etl::to_unsigned(int8_t(-50)); + + auto umin_expected = static_cast(etl::integral_limits::min); + auto umax_expected = static_cast(etl::integral_limits::max); + auto uzero_expected = static_cast(0); + auto uplus_expected = static_cast(50); + auto uminus_expected = static_cast(-50); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(int(umin_expected), int(umin)); + CHECK_EQUAL(int(umax_expected), int(umax)); + CHECK_EQUAL(int(uzero_expected), int(uzero)); + CHECK_EQUAL(int(uplus_expected), int(uplus)); + CHECK_EQUAL(int(uminus_expected), int(uminus)); + } + + //************************************************************************* + TEST(test_to_unsigned_from_int16_t) + { + auto umin = etl::to_unsigned(etl::integral_limits::min); + auto umax = etl::to_unsigned(etl::integral_limits::max); + auto uzero = etl::to_unsigned(int16_t(0)); + auto uplus = etl::to_unsigned(int16_t(50)); + auto uminus = etl::to_unsigned(int16_t(-50)); + + auto umin_expected = static_cast(etl::integral_limits::min); + auto umax_expected = static_cast(etl::integral_limits::max); + auto uzero_expected = static_cast(0); + auto uplus_expected = static_cast(50); + auto uminus_expected = static_cast(-50); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(umin_expected, umin); + CHECK_EQUAL(umax_expected, umax); + CHECK_EQUAL(uzero_expected, uzero); + CHECK_EQUAL(uplus_expected, uplus); + CHECK_EQUAL(uminus_expected, uminus); + } + + //************************************************************************* + TEST(test_to_unsigned_from_int32_t) + { + auto umin = etl::to_unsigned(etl::integral_limits::min); + auto umax = etl::to_unsigned(etl::integral_limits::max); + auto uzero = etl::to_unsigned(int32_t(0)); + auto uplus = etl::to_unsigned(int32_t(50)); + auto uminus = etl::to_unsigned(int32_t(-50)); + + auto umin_expected = static_cast(etl::integral_limits::min); + auto umax_expected = static_cast(etl::integral_limits::max); + auto uzero_expected = static_cast(0); + auto uplus_expected = static_cast(50); + auto uminus_expected = static_cast(-50); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(umin_expected, umin); + CHECK_EQUAL(umax_expected, umax); + CHECK_EQUAL(uzero_expected, uzero); + CHECK_EQUAL(uplus_expected, uplus); + CHECK_EQUAL(uminus_expected, uminus); + } + + //************************************************************************* + TEST(test_to_unsigned_from_int64_t) + { + auto umin = etl::to_unsigned(etl::integral_limits::min); + auto umax = etl::to_unsigned(etl::integral_limits::max); + auto uzero = etl::to_unsigned(int64_t(0)); + auto uplus = etl::to_unsigned(int64_t(50)); + auto uminus = etl::to_unsigned(int64_t(-50)); + + auto umin_expected = static_cast(etl::integral_limits::min); + auto umax_expected = static_cast(etl::integral_limits::max); + auto uzero_expected = static_cast(0); + auto uplus_expected = static_cast(50); + auto uminus_expected = static_cast(-50); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(umin_expected, umin); + CHECK_EQUAL(umax_expected, umax); + CHECK_EQUAL(uzero_expected, uzero); + CHECK_EQUAL(uplus_expected, uplus); + CHECK_EQUAL(uminus_expected, uminus); + } + + //************************************************************************* + TEST(test_to_signed_from_uint8_t) + { + auto smin = etl::to_signed(etl::integral_limits::min); + auto smax = etl::to_signed(etl::integral_limits::max); + auto splus = etl::to_signed(uint8_t(50)); + auto sminus = etl::to_signed(uint8_t(206)); + + auto smin_expected = static_cast(etl::integral_limits::min); + auto smax_expected = static_cast(etl::integral_limits::max); + auto splus_expected = static_cast(50); + auto sminus_expected = static_cast(206); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(int(smin_expected), int(smin)); + CHECK_EQUAL(int(smax_expected), int(smax)); + CHECK_EQUAL(int(splus_expected), int(splus)); + CHECK_EQUAL(int(sminus_expected), int(sminus)); + } + + //************************************************************************* + TEST(test_to_signed_from_uint16_t) + { + auto smin = etl::to_signed(etl::integral_limits::min); + auto smax = etl::to_signed(etl::integral_limits::max); + auto splus = etl::to_signed(uint16_t(50)); + auto sminus = etl::to_signed(uint16_t(65486)); + + auto smin_expected = static_cast(etl::integral_limits::min); + auto smax_expected = static_cast(etl::integral_limits::max); + auto splus_expected = static_cast(50); + auto sminus_expected = static_cast(65486); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(smin_expected, smin); + CHECK_EQUAL(smax_expected, smax); + CHECK_EQUAL(splus_expected, splus); + CHECK_EQUAL(sminus_expected, sminus); + } + + //************************************************************************* + TEST(test_to_signed_from_uint32_t) + { + auto smin = etl::to_signed(etl::integral_limits::min); + auto smax = etl::to_signed(etl::integral_limits::max); + auto splus = etl::to_signed(uint32_t(50)); + auto sminus = etl::to_signed(uint32_t(4294967246U)); + + auto smin_expected = static_cast(etl::integral_limits::min); + auto smax_expected = static_cast(etl::integral_limits::max); + auto splus_expected = static_cast(50); + auto sminus_expected = static_cast(4294967246U); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(smin_expected, smin); + CHECK_EQUAL(smax_expected, smax); + CHECK_EQUAL(splus_expected, splus); + CHECK_EQUAL(sminus_expected, sminus); + } + + //************************************************************************* + TEST(test_to_signed_from_uint64_t) + { + auto smin = etl::to_signed(etl::integral_limits::min); + auto smax = etl::to_signed(etl::integral_limits::max); + auto splus = etl::to_signed(uint64_t(50)); + auto sminus = etl::to_signed(uint64_t(18446744073709551566ULL)); + + auto smin_expected = static_cast(etl::integral_limits::min); + auto smax_expected = static_cast(etl::integral_limits::max); + auto splus_expected = static_cast(50); + auto sminus_expected = static_cast(18446744073709551566ULL); + + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + CHECK((etl::is_same::value)); + + CHECK_EQUAL(smin_expected, smin); + CHECK_EQUAL(smax_expected, smax); + CHECK_EQUAL(splus_expected, splus); + CHECK_EQUAL(sminus_expected, sminus); + } + + //************************************************************************* + TEST(test_to_unsigned_constexpr) + { + const int8_t value = static_cast(-50); + + constexpr auto Size = etl::to_unsigned(value); + + CHECK_EQUAL(static_cast(value), Size); + + int a[Size] = {0}; // Use Size in a context that requires a constant expression + (void)a; // Avoid unused variable warning + } + + //************************************************************************* + TEST(test_to_signed_constexpr) + { + const uint8_t value = static_cast(50); + + constexpr auto Size = etl::to_signed(value); + + CHECK_EQUAL(static_cast(value), Size); + + int a[Size] = {0}; // Use Size in a context that requires a constant expression + (void)a; // Avoid unused variable warning + } } } // namespace