diff options
-rw-r--r-- | include/armnn/utility/NumericCast.hpp | 166 | ||||
-rw-r--r-- | src/armnn/test/UtilityTests.cpp | 80 |
2 files changed, 225 insertions, 21 deletions
diff --git a/include/armnn/utility/NumericCast.hpp b/include/armnn/utility/NumericCast.hpp index af34013c30..cdbfd89638 100644 --- a/include/armnn/utility/NumericCast.hpp +++ b/include/armnn/utility/NumericCast.hpp @@ -22,14 +22,16 @@ namespace armnn #if defined(ARMNN_NUMERIC_CAST_TESTABLE) # define ARMNN_NUMERIC_CAST_CHECK(cond, msg) ConditionalThrow<std::bad_cast>(cond) #else -# define ARMNN_NUMERIC_CAST_CHECK(cond, msg) ARMNN_ASSERT_MSG(cond, msg) +#define ARMNN_NUMERIC_CAST_CHECK(cond, msg) ARMNN_ASSERT_MSG(cond, msg) #endif +// Unsigned to Unsigned + template<typename Dest, typename Source> typename std::enable_if_t< std::is_unsigned<Source>::value && - std::is_unsigned<Dest>::value - , Dest> + std::is_unsigned<Dest>::value, + Dest> numeric_cast(Source source) { #if ENABLE_NUMERIC_CAST_CHECKS @@ -43,26 +45,53 @@ numeric_cast(Source source) return static_cast<Dest>(source); } +// Signed to Signed + +// numeric cast from signed integral to signed integral types, checked for narrowing overflows template<typename Dest, typename Source> typename std::enable_if_t< std::is_signed<Source>::value && - std::is_signed<Dest>::value - , Dest> + std::is_integral<Source>::value && + std::is_signed<Dest>::value && + std::is_integral<Dest>::value, + Dest> numeric_cast(Source source) { - static_assert(!std::is_floating_point<Source>::value && !std::is_floating_point<Dest>::value, - "numeric_cast doesn't cast float."); +#if ENABLE_NUMERIC_CAST_CHECKS + if (source > std::numeric_limits<Dest>::max()) + { + ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting signed integral type to narrower signed type. " + "Overflow detected."); + } + + if (source < std::numeric_limits<Dest>::lowest()) + { + ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting signed integral type to narrower signed type. " + "Underflow detected."); + } +#endif // ENABLE_NUMERIC_CAST_CHECKS + + return static_cast<Dest>(source); +} +// numeric cast from floating point to floating point types, checked for narrowing overflows +template<typename Dest, typename Source> +typename std::enable_if_t< + std::is_floating_point<Source>::value && + std::is_floating_point<Dest>::value, + Dest> +numeric_cast(Source source) +{ #if ENABLE_NUMERIC_CAST_CHECKS if (source > std::numeric_limits<Dest>::max()) { - ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting signed type to narrower signed type. " + ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting floating point type to narrower signed type. " "Overflow detected."); } if (source < std::numeric_limits<Dest>::lowest()) { - ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting signed type to narrower signed type. " + ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting floating point type to narrower signed type. " "Underflow detected."); } #endif // ENABLE_NUMERIC_CAST_CHECKS @@ -70,16 +99,71 @@ numeric_cast(Source source) return static_cast<Dest>(source); } -// numeric cast from unsigned to signed checked for narrowing overflows +// numeric cast from floating point types (signed) to signed integral types, checked for narrowing overflows template<typename Dest, typename Source> typename std::enable_if_t< + std::is_floating_point<Source>::value && std::is_signed<Dest>::value && - std::is_unsigned<Source>::value - , Dest> -numeric_cast(Source sValue) + std::is_integral<Dest>::value, + Dest> +numeric_cast(Source source) { - static_assert(!std::is_floating_point<Dest>::value, "numeric_cast doesn't cast to float."); +#if ENABLE_NUMERIC_CAST_CHECKS + if (source > static_cast<Source>(std::numeric_limits<Dest>::max())) + { + ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting floating point type to narrower signed type. " + "Overflow detected."); + } + + if (source < static_cast<Source>(std::numeric_limits<Dest>::lowest())) + { + ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting floating point type to narrower signed type. " + "Underflow detected."); + } +#endif // ENABLE_NUMERIC_CAST_CHECKS + + return static_cast<Dest>(source); +} + +// numeric cast from signed integral types to floating point types (signed), checked for narrowing overflows +template<typename Dest, typename Source> +typename std::enable_if_t< + std::is_signed<Source>::value && + std::is_integral<Source>::value && + std::is_floating_point<Dest>::value, + Dest> +numeric_cast(Source source) +{ +#if ENABLE_NUMERIC_CAST_CHECKS + Dest sourceConverted = static_cast<Dest>(source); + + if (sourceConverted > std::numeric_limits<Dest>::max()) + { + ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting signed type to narrower floating point type. " + "Overflow detected."); + } + + if (sourceConverted < std::numeric_limits<Dest>::lowest()) + { + ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting signed type to narrower floating point type. " + "Underflow detected."); + } +#endif // ENABLE_NUMERIC_CAST_CHECKS + + return static_cast<Dest>(source); +} + +// Unsigned to Signed +// numeric cast from unsigned integral type to signed integral type, checked for narrowing overflows +template<typename Dest, typename Source> +typename std::enable_if_t< + std::is_signed<Dest>::value && + std::is_integral<Dest>::value && + std::is_unsigned<Source>::value, + Dest> +numeric_cast(Source sValue) +{ #if ENABLE_NUMERIC_CAST_CHECKS if (sValue > static_cast< typename std::make_unsigned<Dest>::type >(std::numeric_limits<Dest>::max())) { @@ -91,17 +175,36 @@ numeric_cast(Source sValue) return static_cast<Dest>(sValue); } -// numeric cast from signed to unsigned checked for underflows and narrowing overflows +// numeric cast from unsigned integral type to floating point (signed), checked for narrowing overflows template<typename Dest, typename Source> typename std::enable_if_t< - std::is_unsigned<Dest>::value && - std::is_signed<Source>::value - , Dest> + std::is_floating_point<Dest>::value && + std::is_unsigned<Source>::value, + Dest> numeric_cast(Source sValue) { - static_assert(!std::is_floating_point<Source>::value && !std::is_floating_point<Dest>::value, - "numeric_cast doesn't cast floats."); +#if ENABLE_NUMERIC_CAST_CHECKS + if (static_cast<Dest>(sValue) > std::numeric_limits<Dest>::max()) + { + ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting unsigned type to floating point type. " + "Overflow detected."); + } +#endif // ENABLE_NUMERIC_CAST_CHECKS + + return static_cast<Dest>(sValue); +} + +// Signed to Unsigned +// numeric cast from signed integral types to unsigned integral type, checked for underflows and narrowing overflows +template<typename Dest, typename Source> +typename std::enable_if_t< + std::is_unsigned<Dest>::value && + std::is_signed<Source>::value && + std::is_integral<Source>::value, + Dest> +numeric_cast(Source sValue) +{ #if ENABLE_NUMERIC_CAST_CHECKS if (sValue < 0) { @@ -114,7 +217,30 @@ numeric_cast(Source sValue) ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting signed type to unsigned type. " "Overflow detected."); } +#endif // ENABLE_NUMERIC_CAST_CHECKS + return static_cast<Dest>(sValue); +} +// numeric cast from floating point (signed) to unsigned integral type, checked for underflows and narrowing overflows +template<typename Dest, typename Source> +typename std::enable_if_t< + std::is_unsigned<Dest>::value && + std::is_floating_point<Source>::value, + Dest> +numeric_cast(Source sValue) +{ +#if ENABLE_NUMERIC_CAST_CHECKS + if (sValue < 0) + { + ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting negative value to unsigned type. " + "Underflow detected."); + } + + if (sValue > static_cast<Source>(std::numeric_limits<Dest>::max())) + { + ARMNN_NUMERIC_CAST_CHECK(false, "numeric_cast failed casting floating point type to unsigned type. " + "Overflow detected."); + } #endif // ENABLE_NUMERIC_CAST_CHECKS return static_cast<Dest>(sValue); } diff --git a/src/armnn/test/UtilityTests.cpp b/src/armnn/test/UtilityTests.cpp index 8d933eb435..bad6c2250b 100644 --- a/src/armnn/test/UtilityTests.cpp +++ b/src/armnn/test/UtilityTests.cpp @@ -14,6 +14,8 @@ #include <armnn/Exceptions.hpp> +#include <limits> + // Tests of include/Utility files BOOST_AUTO_TEST_SUITE(UtilityTests) @@ -160,6 +162,9 @@ BOOST_AUTO_TEST_CASE(NumericCast) BOOST_CHECK_NO_THROW(numeric_cast<int16_t>(1L << 7)); BOOST_CHECK_NO_THROW(numeric_cast<int16_t>((1L << 15)*-1)); + BOOST_CHECK_NO_THROW(numeric_cast<int16_t>(1U << 8)); + BOOST_CHECK_NO_THROW(numeric_cast<int16_t>(1U << 14)); + // To 32 bit BOOST_CHECK_NO_THROW(numeric_cast<uint32_t>(1)); BOOST_CHECK_NO_THROW(numeric_cast<uint32_t>(1 << 8)); @@ -170,6 +175,79 @@ BOOST_AUTO_TEST_CASE(NumericCast) BOOST_CHECK_NO_THROW(numeric_cast<int32_t>((1L << 8)*-1)); BOOST_CHECK_NO_THROW(numeric_cast<int32_t>((1L << 16)*-1)); BOOST_CHECK_NO_THROW(numeric_cast<int32_t>((1LL << 31)*-1)); -} + + BOOST_CHECK_NO_THROW(numeric_cast<int32_t>(1U)); + BOOST_CHECK_NO_THROW(numeric_cast<int32_t>(1U << 8)); + BOOST_CHECK_NO_THROW(numeric_cast<int32_t>(1U << 16)); + BOOST_CHECK_NO_THROW(numeric_cast<int32_t>(1U << 30)); + + float float_max = std::numeric_limits<float>::max(); + float float_min = std::numeric_limits<float>::lowest(); + auto int8_max = std::numeric_limits<int8_t>::max(); + auto int16_max = std::numeric_limits<int16_t>::max(); + auto int32_max = std::numeric_limits<int32_t>::max(); + auto int8_min = std::numeric_limits<int8_t>::lowest(); + auto int16_min = std::numeric_limits<int16_t>::lowest(); + auto int32_min = std::numeric_limits<int32_t>::lowest(); + auto uint8_max = std::numeric_limits<uint8_t>::max(); + auto uint16_max = std::numeric_limits<uint16_t>::max(); + auto uint32_max = std::numeric_limits<uint32_t>::max(); + auto double_max = std::numeric_limits<double>::max(); + + // Float to signed integer + BOOST_CHECK_NO_THROW(numeric_cast<int32_t>(1.324f)); + BOOST_CHECK(1 == numeric_cast<int32_t>(1.324f)); + BOOST_CHECK_NO_THROW(numeric_cast<int32_t>(-1.0f)); + BOOST_CHECK(-1 == numeric_cast<int32_t>(-1.0f)); + + BOOST_CHECK_NO_THROW(numeric_cast<int8_t>(static_cast<float>(int8_max))); + BOOST_CHECK_NO_THROW(numeric_cast<int16_t>(static_cast<float>(int16_max))); + BOOST_CHECK_NO_THROW(numeric_cast<int32_t>(static_cast<float>(int32_max))); + + BOOST_CHECK_THROW(numeric_cast<int8_t>(float_max), std::bad_cast); + BOOST_CHECK_THROW(numeric_cast<int16_t>(float_max), std::bad_cast); + BOOST_CHECK_THROW(numeric_cast<int32_t>(float_max), std::bad_cast); + + BOOST_CHECK_THROW(numeric_cast<int8_t>(float_min), std::bad_cast); + BOOST_CHECK_THROW(numeric_cast<int16_t>(float_min), std::bad_cast); + BOOST_CHECK_THROW(numeric_cast<int32_t>(float_min), std::bad_cast); + + // Signed integer to float + BOOST_CHECK_NO_THROW(numeric_cast<float>(1)); + BOOST_CHECK(1.0 == numeric_cast<float>(1)); + BOOST_CHECK_NO_THROW(numeric_cast<float>(-1)); + BOOST_CHECK(-1.0 == numeric_cast<float>(-1)); + + BOOST_CHECK_NO_THROW(numeric_cast<float>(int8_max)); + BOOST_CHECK_NO_THROW(numeric_cast<float>(int16_max)); + BOOST_CHECK_NO_THROW(numeric_cast<float>(int32_max)); + + BOOST_CHECK_NO_THROW(numeric_cast<float>(int8_min)); + BOOST_CHECK_NO_THROW(numeric_cast<float>(int16_min)); + BOOST_CHECK_NO_THROW(numeric_cast<float>(int32_min)); + + // Unsigned integer to float + BOOST_CHECK_NO_THROW(numeric_cast<float>(1U)); + BOOST_CHECK(1.0 == numeric_cast<float>(1U)); + + BOOST_CHECK_NO_THROW(numeric_cast<float>(uint8_max)); + BOOST_CHECK_NO_THROW(numeric_cast<float>(uint16_max)); + BOOST_CHECK_NO_THROW(numeric_cast<float>(uint32_max)); + + // Float to unsigned integer + BOOST_CHECK_NO_THROW(numeric_cast<uint32_t>(1.43243f)); + BOOST_CHECK(1 == numeric_cast<uint32_t>(1.43243f)); + + BOOST_CHECK_THROW(numeric_cast<uint32_t>(-1.1f), std::bad_cast); + BOOST_CHECK_THROW(numeric_cast<uint32_t>(float_max), std::bad_cast); + + // Double checks + BOOST_CHECK_THROW(numeric_cast<int32_t>(double_max), std::bad_cast); + BOOST_CHECK_THROW(numeric_cast<int32_t>(double_max), std::bad_cast); + BOOST_CHECK_THROW(numeric_cast<float>(double_max), std::bad_cast); + BOOST_CHECK_NO_THROW(numeric_cast<double>(int32_max)); + BOOST_CHECK_NO_THROW(numeric_cast<long double>(int32_max)); + + } BOOST_AUTO_TEST_SUITE_END() |