Using gcc version 4.8.4 on Linux, short is 16 bit and int is 32 bit.
#include "stdio.h"
int main( void ){
unsigned short u = 0xaabb;
unsigned int v = 0xaabb;
printf ("%08x %08x\n", u, (unsigned short)((u*0x10001)/0x100));
printf ("%08x %08x\n", v, (unsigned short)((v*0x10001)/0x100));
return 0;
}
Result:
0000aabb 0000bbab
0000aabb 0000bbaa
This can be varied, e.g., by dividing with 0x10, which produces a similar result (+1) for the first case. The effect does not occur if the byte truncated by /0x100 is less than 0x80. Machine code for the first case (short u) looks as if some rounding (addition of 0xFF) is intended.
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.)
Usual arithmetic conversions at play.
u is converted to int before multiplication. Since int is signed it behaves differently on division.
printf("%08x\n", (u*0x10001)/0x100);
printf("%08x\n", (v*0x10001)/0x100);
Returns
ffaabbab
00aabbaa
Strictly speaking multiplication overflow on signed integer is already undefined behaviour, so result is invalid even before the division.
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