bbg
bbg

Reputation: 3072

interpret signed as unsigned

I have a value like this:

int64_t s_val = SOME_SIGNED_VALUE;

How can I get a

uint64_t u_val

that has exactly the same bit pattern as s_val, but is treated as unsigned?

This may be really simple, but after looking on Stackoverflow and elsewhere I haven't turned up the answer.

Upvotes: 34

Views: 25520

Answers (7)

ssell
ssell

Reputation: 6597

Wanted to share this C++14 modern, generic solution. Originally demonstrated here.

template<class T> 
auto as_unsigned(T t) 
{ 
    return std::make_unsigned_t<T>(t); 
}

Which may be used as follows:

auto sx = int32_t{ 55 };
auto ux = as_unsigned(sx);

You can see it in action here.

Upvotes: 6

Michael Burr
Michael Burr

Reputation: 340198

Note that you don't need the cast at all. For all the wrangling about whether the cast will munge bits or not for negative representations, one thing has gotten lost - the cast is completely unnecessary.

Because of the conversions that C/C++ will do (and how casting is defined), this:

int64_t s_val = SOME_SIGNED_VALUE;
uint64_t u_val = s_val;

is exactly equivalent to:

int64_t s_val = SOME_SIGNED_VALUE;
uint64_t u_val = static_cast<uint64_t>(s_val);

That said, you might still want the cast because it signals intent. However, I've heard it argued that you shouldn't use unnecessary casts because it can silence the compiler in situations where you might want a warning.

Pick your poison.

Upvotes: 11

Raven
Raven

Reputation: 1331

I agree static_cast is appropriate in this case, but no one has mentioned a very similar looking case where static_cast wouldn't preserve bits as might be expected.

char x = -1; // 255
unsigned int x2 = static_cast<unsigned int>(x); // 4294967295
unsigned int x3 = static_cast<unsigned int>(static_cast<unsigned char>(x)); // 255

Watch out for sign extension when you are casting from a small signed value to a large unsigned value. Possibly other combinations are vulnerable too - I haven't thought it all the way through.

Upvotes: 10

int3
int3

Reputation: 13201

Generally speaking, it doesn't matter whether you use static_cast<int64_t> or reinterpret_cast<int64_t>. So long as you are running on a processor that uses two's complement to represent negative numbers, the result is the same. (Practically all modern processors use that.) Under two's complement, a positive number in a signed int is represented the same way in an unsigned int; if it's a negative number it'll be reinterpreted as a large positive number in the unsigned form.

Basically, what your cast does is tell the compiler to produce different assembly instructions when dealing with that value. E.g. there are different instructions for multiplication and division for signed integers. Although addition and subtraction remains the same (read the wikipedia link and you'll understand).

Upvotes: 7

Kirill V. Lyadvinsky
Kirill V. Lyadvinsky

Reputation: 99565

int64_t s_val = SOME_SIGNED_VALUE;
uint64_t u_val = static_cast<uint64_t>(s_val);

C++ Standard 4.7/2 states that:

If the destination type is unsigned, the resulting value is the least unsigned integer congruent to the source integer (modulo 2n where n is the number of bits used to represent the unsigned type). [Note: In a two’s complement representation, this conversion is conceptual and there is no change in the bit pattern (if there is no truncation). ]

From the other hand, Standard says that "The mapping performed by reinterpret_cast is implementation-defined. [Note: it might, or might not, produce a representation different from the original value. ]" (5.2.10/3). So, I'd recommend to use static_cast.

Upvotes: 39

AnT stands with Russia
AnT stands with Russia

Reputation: 320421

Logical bit pattern (bits of value-representation), i.e. values of binary digits can only be preserved if the original signed value was non-negative, because negative values cannot be represented by an unsigned integer variable. All you need to do is to assign your signed value to your unsigned integral object and you are done

uint64_t u_val = s_val;

An explicit cast is not necessary, but might be used to suppress compiler warnings.

As for physical bit pattern (i.e. what you see in raw memory, bits of object-representation), you simply can't "convert" it that way. C++ language does not provide you with any methods of conversion that would guarantee to preserve the physical bit pattern. All you can do is to reinterpret the memory occupied by the signed object as an unsigned object of the same size

STATIC_ASSERT(sizeof(int64_t) == sizeof(uint64_t));
uint64_t u_val = reinterpret_cast<uint64_t&>(s_val);

Again, this is not a conversion, but rather a memory reinterpretation. This is not guaranteed to work and this is generally illegal.

Upvotes: 5

xtofl
xtofl

Reputation: 41509

You can also reinterpret_cast it, or use a union:

union {
   int64_t i64;
   uint64_t ui64;
} variable;

variable.i64 = SOME_SIGNED_VALUE;
uint64_t a_copy = variable.ui64;

Upvotes: 2

Related Questions