A literal like 0x10001
will be of type int
(if it can fit inside an int, which is true in this case). int
is a signed type.
Since the variable u
is a small integer type, it gets integer promoted to int
whenever used in an expression.
0xaabb * 0x10001
would supposedly give the result 0xAABBAABB
. However, that result is too large to fit inside an int
on a 32 bit two's complement system, where the largest number for an int
is 0x7FFFFFFF
. So you get an overflow on a signed integer and therefore invoke undefined behavior - anything can happen.
Never use signed integers when doing any form of binary arithmetic!
Furthermore, the final cast to (unsigned short)
is futile, because printf argument promotes the passed value to int
anyhow. Which is strictly speaking incorrect too, because %x
means that printf
expects an unsigned int
.
To avoid all trouble with the unpredictable and limited default integer types in C, use stdint.h
instead. Also, using unsigned int literals solves a lot of implicit type promotion bugs.
Example:
#include <stdio.h>
#include <stdint.h>
#include <inttypes.h>
int main( void ){
uint16_t u = 0xaabb;
uint16_t v = 0xaabb;
printf ("%08" PRIx16 " %08" PRIx16 "\n", u, (uint16_t)(u*0x10001u/0x100u));
printf ("%08" PRIx16 " %08" PRIx16 "\n", v, (uint16_t)(v*0x10001u/0x100u));
return 0;
}
(This code will have argument promotion too, but by using the PRIx16
format specifier, you tell printf
that it is now the compiler's business to make the code work, regardless of what type promotions that might be present in the function call.)