简体   繁体   中英

Make signed int value from unsigned int value and back

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM