maths: add signed overloads for renormalise

This commit is contained in:
Danny Robson 2017-05-24 15:15:25 +10:00
parent 25e19b5810
commit da00e77e7e
2 changed files with 77 additions and 12 deletions

View File

@ -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 <typename T, typename U>
constexpr
typename std::enable_if<
!std::is_floating_point<T>::value && std::is_floating_point<U>::value, U
std::is_unsigned<T>::value && std::is_floating_point<U>::value, U
>::type
renormalise (T t)
{
@ -675,11 +680,11 @@ namespace util {
//-------------------------------------------------------------------------
// float -> int
// float -> uint
template <typename T, typename U>
constexpr
typename std::enable_if<
std::is_floating_point<T>::value && !std::is_floating_point<U>::value, U
std::is_floating_point<T>::value && std::is_unsigned<U>::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 <typename T, typename U>
constexpr
@ -724,12 +729,12 @@ namespace util {
//-------------------------------------------------------------------------
// hi_int -> lo_int
// hi_uint -> lo_uint
template <typename T, typename U>
constexpr
typename std::enable_if<
std::is_integral<T>::value &&
std::is_integral<U>::value &&
std::is_unsigned<T>::value &&
std::is_unsigned<U>::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 <typename T, typename U>
constexpr
typename std::enable_if<
std::is_integral<T>::value &&
std::is_integral<U>::value &&
std::is_unsigned<T>::value &&
std::is_unsigned<U>::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 <typename T, typename U>
constexpr
typename std::enable_if<
@ -782,6 +789,46 @@ namespace util {
>::type
renormalise (T t)
{ return t; }
//-------------------------------------------------------------------------
// anything-to-sint
template <typename T, typename U>
constexpr
typename std::enable_if<
std::is_signed<U>::value &&
std::is_integral<U>::value &&
!std::is_same<T,U>::value,
U
>::type
renormalise (T t)
{
using uint_t = typename std::make_unsigned<U>::type;
return static_cast<U> (
::util::renormalise<T,uint_t> (t) + std::numeric_limits<U>::min ()
);
};
//-------------------------------------------------------------------------
// sint-to-anything
template <typename T, typename U>
constexpr
typename std::enable_if<
std::is_signed<T>::value &&
std::is_integral<T>::value &&
!std::is_same<T,U>::value,
U
>::type
renormalise (T sint)
{
using uint_t = typename std::make_unsigned<T>::type;
return ::util::renormalise<uint_t,U> (
static_cast<uint_t> (sint) - std::numeric_limits<T>::min ()
);
};
}

View File

@ -122,6 +122,24 @@ test_normalisations (util::TAP::logger &tap)
tap.expect_eq (util::renormalise<uint8_t,uint32_t> (0x00), 0x00000000u, "normalise lo u8-to-u32");
tap.expect_eq (util::renormalise<uint32_t,uint8_t> (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<int32_t>::min (), std::numeric_limits<uint32_t>::min (), "min" },
{ std::numeric_limits<int32_t>::max (), std::numeric_limits<uint32_t>::max (), "max" },
{ 0, std::numeric_limits<uint32_t>::max () / 2u + 1, "mid" },
};
for (const auto &t: TESTS) {
tap.expect_eq (util::renormalise<int32_t,uint32_t> (t.sint), t.uint, "%s s32-to-u32", t.msg);
tap.expect_eq (util::renormalise<uint32_t,int32_t> (t.uint), t.sint, "%s u32-to-s32", t.msg);
}
}
}