To extract the upper and lower word of an unsigned 32-bit integer and store each one in separate uint16_t-variables I do as follows (nbr is an unsigned 32-bit integer):
uint16_t lower_word = (uint16_t) nbr & 0x0000FFFF; // mask desired word
uint16_t upper_word = (uint16_t) ((nbr & 0xFFFF0000) >> 16); // right-shift after masking
Is the explicit conversion to an uint16_t unnecessary? What other more effective ways, if there are any, do you recommend to obtain the desired results instead of this approach?
The C type system is both subtle and dangerous. The explicit conversion may or may not be necessary. In case of (uint16_t) nbr & 0x0000FFFF
specifically, the cast is not correct, assuming 32 bit CPU.
You cast before the operation takes place. Meaning that the operand nbr
will get explicitly converted by the cast, and then immediately implicitly converted up to int
by implicit integer promotion. The result will be of type int
, which is signed. Harmless in this case but can cause trouble in other cases. By using the incorrect cast you made a signed int
out of a uint32_t
which was not the intention.
Overall you need to be aware of Implicit type promotion rules.
Although, there is an implicit lvalue conversion upon assignment back to uint16_t
, which most of the time saves the day.
Also note that 0x0000FFFF
is dangerous style. Hex literals are of the type where the value will fit, regardless of how many zeroes you put before the value. In this case, it is int
which is signed. On a 16 bit system, 0x0000FFFF
would give int
but 0x00008000
would give unsigned int
. (Check this weird bug for example: Why is 0 < -0x80000000?)
The best practice, rugged, portable, MISRA-C compliant code, is code which does not contain any implicit conversions at all:
uint32_t nbr = ...;
uint16_t lower_word = (uint16_t) (nbr & 0xFFFFUL);
uint16_t upper_word = (uint16_t) ((nbr >> 16) & 0xFFFFUL);
This assuming nbr
is known to be uint32_t
, otherwise it is best practice to cast that operand to uint32_t
before casts.
The masks are not really necessary in this specific case, but in the general case, for example when masking out 4 bytes from a uint32_t
.
uint16_t lower_word = (uint16_t) nbr;
uint16_t upper_word = (uint16_t) (nbr >> 16);
masks are useless
cast are necessary else the compiler could produce warning
{edit to take into account the remark of Lundin / Eric Postpischil}
for instance gcc -Wconversion
produces a warning without the cast
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With