diff --git a/maths.hpp b/maths.hpp index 6ed4e4d5..34632e5a 100644 --- a/maths.hpp +++ b/maths.hpp @@ -660,13 +660,18 @@ namespace util { /////////////////////////////////////////////////////////////////////////// - // renormalisation of unit floating point and/or normalised integers + /// convert between different representations of normalised quantities. + /// + /// * floating point values must be within [0, 1] (otherwise undefined) + /// * signed values are handled by converting to unsigned representations + /// * may introduce small biases when expanding values so that low order + /// bits have some meaning (particularly when dealing with UINTMAX) - // int -> float + // uint -> float template constexpr typename std::enable_if< - !std::is_floating_point::value && std::is_floating_point::value, U + std::is_unsigned::value && std::is_floating_point::value, U >::type renormalise (T t) { @@ -675,11 +680,11 @@ namespace util { //------------------------------------------------------------------------- - // float -> int + // float -> uint template constexpr typename std::enable_if< - std::is_floating_point::value && !std::is_floating_point::value, U + std::is_floating_point::value && std::is_unsigned::value, U >::type renormalise (T t) { @@ -708,7 +713,7 @@ namespace util { //------------------------------------------------------------------------- - // float -> float, avoid identity conversion as we don't want to create + // float -> float, avoids identity conversion as we don't want to create // ambiguous overloads template constexpr @@ -724,12 +729,12 @@ namespace util { //------------------------------------------------------------------------- - // hi_int -> lo_int + // hi_uint -> lo_uint template constexpr typename std::enable_if< - std::is_integral::value && - std::is_integral::value && + std::is_unsigned::value && + std::is_unsigned::value && (sizeof (T) > sizeof (U)), U >::type renormalise (T t) @@ -744,12 +749,12 @@ namespace util { //------------------------------------------------------------------------- - // lo_int -> hi_int + // lo_uint -> hi_uint template constexpr typename std::enable_if< - std::is_integral::value && - std::is_integral::value && + std::is_unsigned::value && + std::is_unsigned::value && sizeof (T) < sizeof (U), U >::type renormalise (T t) @@ -775,6 +780,8 @@ namespace util { //------------------------------------------------------------------------- + // identity transformation. must precede the signed cases, as they may rely + // on this as a side effect of casts. template constexpr typename std::enable_if< @@ -782,6 +789,46 @@ namespace util { >::type renormalise (T t) { return t; } + + + //------------------------------------------------------------------------- + // anything-to-sint + template + constexpr + typename std::enable_if< + std::is_signed::value && + std::is_integral::value && + !std::is_same::value, + U + >::type + renormalise (T t) + { + using uint_t = typename std::make_unsigned::type; + + return static_cast ( + ::util::renormalise (t) + std::numeric_limits::min () + ); + }; + + + //------------------------------------------------------------------------- + // sint-to-anything + template + constexpr + typename std::enable_if< + std::is_signed::value && + std::is_integral::value && + !std::is_same::value, + U + >::type + renormalise (T sint) + { + using uint_t = typename std::make_unsigned::type; + + return ::util::renormalise ( + static_cast (sint) - std::numeric_limits::min () + ); + }; } diff --git a/test/maths.cpp b/test/maths.cpp index 24077983..d7572f5d 100644 --- a/test/maths.cpp +++ b/test/maths.cpp @@ -122,6 +122,24 @@ test_normalisations (util::TAP::logger &tap) tap.expect_eq (util::renormalise (0x00), 0x00000000u, "normalise lo u8-to-u32"); tap.expect_eq (util::renormalise (0xffffffff), 0xffu, "normalise hi u32-to-u8"); + + // sint32 to uint32 + { + static const struct { + int32_t sint; + uint32_t uint; + const char *msg; + } TESTS[] = { + { std::numeric_limits::min (), std::numeric_limits::min (), "min" }, + { std::numeric_limits::max (), std::numeric_limits::max (), "max" }, + { 0, std::numeric_limits::max () / 2u + 1, "mid" }, + }; + + for (const auto &t: TESTS) { + tap.expect_eq (util::renormalise (t.sint), t.uint, "%s s32-to-u32", t.msg); + tap.expect_eq (util::renormalise (t.uint), t.sint, "%s u32-to-s32", t.msg); + } + } }