I need to convert a sequence of unsigned integers to the sequence of signed integers and back keeping the order of the elements.
Ideally, i need functions like this
template <typename T>
std::make_signed_t<T> MakeSigned(T val);
template <typename T>
std::make_unsigned_t<T> MakeUnsigned(T val);
what is the right way to implement them?
EDIT1:
Functions MakeSigned and MakeUnsigned should satisfy the following criteria:
For a given pair of parameters a, b and return values a1, b1, if a <= b then a1 <= b1.
and for a given signed a
MakeSigned(MakeUnsigned(a)) == a
EDIT2 :
MakeSigned does this (for a given type uint8_t, uint16_t, uint32_t, uint64_t):
0 => INT_MIN
UINT_MAX => INT_MAX
MakeUnsigned:
INT_MIN => 0
INT_MAX => UINT_MAX
One solution is to subtract/add bias term std::numeric_limits<Signed>::min()
when mapping to/from unsigned
types. The calculation must be done using unsigned arithmetics which wraps without producing undefined behaviour:
#include <iostream>
#include <type_traits>
#include <limits>
#include <cstdint>
template<typename T>
typename std::enable_if_t<std::is_unsigned_v<T>, std::make_signed<T>>::type map_to_signed(T val) {
using U = std::make_signed_t<T>;
return val + std::numeric_limits<U>::min(); // Wrapping unsigned addition.
}
template<typename T>
typename std::enable_if_t<std::is_signed_v<T>, std::make_unsigned<T>>::type map_to_unsigned(T val) {
using U = std::make_unsigned_t<T>;
return static_cast<U>(val) - std::numeric_limits<T>::min(); // Wrapping unsigned subtraction.
}
int main() {
std::cout << map_to_signed(0u) << '\n';
std::cout << map_to_unsigned(map_to_signed(0u)) << "\n\n";
std::cout << std::numeric_limits<int>::max() << '\n';
std::cout << map_to_unsigned(std::numeric_limits<int>::max()) << '\n';
std::cout << map_to_signed(map_to_unsigned(std::numeric_limits<int>::max())) << "\n\n";
std::cout << std::numeric_limits<unsigned>::max() << '\n';
std::cout << map_to_signed(std::numeric_limits<unsigned>::max()) << '\n';
std::cout << map_to_unsigned(map_to_signed(std::numeric_limits<unsigned>::max())) << "\n\n";
std::cout << std::numeric_limits<uint64_t>::max() << '\n';
std::cout << map_to_signed(std::numeric_limits<uint64_t>::max()) << '\n';
std::cout << map_to_unsigned(map_to_signed(std::numeric_limits<uint64_t>::max())) << "\n\n";
}
Outputs:
-2147483648
0
2147483647
4294967295
2147483647
4294967295
2147483647
4294967295
18446744073709551615
9223372036854775807
18446744073709551615
It's interesting that you do not have the requirement that these functions preserve the value of a
and b
. That's good, because if that were so, then they would not be implementable.
So you want to "offset" the values when you convert.
So int(0)
becomes unsigned(std::numeric_limits<int>::max()+1)
, and unsigned(0)
becomes std::numeric_limits<int>::min()
With that, and Maxim's structure, you should be able to write the functions that you need.
Oh, and if you're using INT_MIN
and INT_MAX
instead of numeric_limits
, you're going to fail.
I would simply flip the sign bit:
template <typename T>
std::make_signed_t<T> MakeSigned(T val)
{
return val ^ (1u << (sizeof(T) * 8 - 1));
}
template <typename T>
std::make_unsigned_t<T> MakeUnsigned(T val)
{
return val ^ (1u << (sizeof(T) * 8 - 1));
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.