Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extract upper and lower word of an unsigned 32-bit integer

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?

like image 532
Abdel Aleem Avatar asked Dec 07 '22 13:12

Abdel Aleem


2 Answers

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.

like image 181
Lundin Avatar answered Dec 31 '22 00:12

Lundin


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

like image 32
bruno Avatar answered Dec 31 '22 00:12

bruno