Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert a uint32_t to an int32_t without either a risk of overflow or excessive complexity

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?
like image 897
Demi Avatar asked Apr 18 '16 21:04

Demi


People also ask

Which of the following type conversions is always considered safe with regards to preserving data?

Widening conversions (promotion) Because widening conversions are always safe, the compiler performs them silently and doesn't issue warnings. The following conversions are widening conversions.

What is a uint32_t?

uint32_t is a numeric type that guarantees 32 bits. The value is unsigned, meaning that the range of values goes from 0 to 232 - 1. This. uint32_t* ptr; declares a pointer of type uint32_t* , but the pointer is uninitialized, that is, the pointer does not point to anywhere in particular.

Why do we use uint32_t in network programming why not use unsigned int instead?

And the reason why people use uint32_t rather than the other types is because they usually don't have such hardware to do testing on. (The same is true of int32_t to a lesser extent, and even int and short ). An example of the corner case: Let unsigned short == uint32_t and int == int48_t .

What is Uint_fast?

The uint_fast*_t type simply defines the fastest type for representing a given number of bits. Think about it this way: you define a variable of type short and use it several times in the program, which is totally valid. However, the system you're working on might work more quickly with values of type int .


2 Answers

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).

like image 137
M.M Avatar answered Sep 21 '22 19:09

M.M


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:

  • Is type-punning through a union unspecified in C99, and has it become specified in C11?
like image 27
a3f Avatar answered Sep 19 '22 19:09

a3f