How can I best convert a uint32_t
to an int32_t
quickly with wrapping, in C++?
Some tries:
uint32_t y = UINT32_MAX;
int32_t x = (int32_t)y; // UB on overflow
int32_t x = *(int32_t*)&y; // does this violate strict aliasing?
How can I best convert a uint32_t
to an int32_t
quickly with wrapping, in C++?
Some tries:
uint32_t y = UINT32_MAX;
int32_t x = (int32_t)y; // UB on overflow
int32_t x = *(int32_t*)&y; // does this violate strict aliasing?
int32_t x = (int32_t)y;
is not overflow and not UB. Overflow is when an arithmetic operation produces a result outside the range of representable values. However, a conversion is not an arithmetic operation.
This situation is implementation-defined behaviour. All implementations that I'm aware of define the behaviour as making no change in the representation.
Note that no cast is necessary here. You can write int32_t x = y;
.
In practical terms, this is simpler and will always work. So much code relies on this that no vendor is ever going to define any other behaviour (not that they have any reason to do so anyway).
int32_t x = *(int32_t*)&y
is not UB. It does not violate strict aliasing because the signed version of a type is allowed to alias the unsigned version. This code is guaranteed to produce the int32_t
with the same representation as the corresponding uint32_t
(i.e. "wrapping", since these types are guaranteed to be 2's complement).
union {
int32_t i;
uint32_t u;
} u;
u.i = ...;
printf("%" PRIu32 "\n", u.u);
This and memcpy(&uint_var, &int_var, sizeof uint_var)
are the two standard ways to do such a conversion without invoking undefined behavior.
See also:
#include <assert.h>
#include <limits.h>
#include <stdlib.h>
int makesigned(unsigned x) {
if (x <= (unsigned) INT_MAX) {
return (int) x;
}
/* assume 2's complement */
if (x >= (unsigned) INT_MIN) {
return 0 - (int)(-x);
}
abort();
return 0;
}
int main(void) {
assert(makesigned(0) == 0);
assert(makesigned(INT_MAX) == INT_MAX);
assert(makesigned(UINT_MAX) == -1);
assert(makesigned(INT_MIN) == INT_MIN);
return 0;
}