The follow derives from the devkitpro 3ds filter audio example though it's not relevant to know exactly how the example works.
In short, consider this code:
size_t data_size = ...;
uint32_t data* = (uint32_t*) malloc(data_size);
for (int i = 0; i < data_size; i++){
int16_t value = 0xBEEF;
// IMPORTANT:
data[i] = (value << 16) | (value & 0xFFFF)
// This results in
// data[i] & 0x0000FFFF == value AND
// data[i] & 0xFFFF0000 == value
}
In this sample, value is shifted left 16 bits despite being a 16 bit type, this should result in zero but it doesn't, presumably because value is cast to uint32 BEFORE the bitwise operation.
What is causing value to be cast to 32 bit BEFORE the bitwise operation value << 16?
Is it data[i] being 32 bit or the 16 literal being 32 bit? Is that even the cause here?
This happened as the result of integer promotions. In most cases where an integer type smaller than int is used in an expression, it is first promoted to type int. On most platforms, this is a 32-bit type.
This is spelled out in section 6.3.1.1p2 of the C24 (draft) standard:
The following may be used in an expression wherever an
intorunsigned intmay be used:
- An object or expression with an integer type (other than
intorunsigned int) whose integer conversion rank is less than or equal to the rank ofintandunsigned int.- A bit-field of type
bool,int,signed int, orunsigned int.The value from a bit-field of a bit-precise integer type is converted to the corresponding bit-precise integer type. If the original type is not a bit-precise integer type (6.2.5): if an
intcan represent all values of the original type (as restricted by the width, for a bit-field), the value is converted to anintotherwise, it is converted to anunsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions
So assuming an int is at least 32 bits on your platform, a uint16_t has a smaller rank and therefore the value is promoted to type int.
There is still a problem however. In the initialization int16_t value = 0xBEEF, the value to set doesn't fit in a int16_t and so gets converted to a negative value. This negative value is promoted to type int in the expression value << 16 and that negative value is left shifted. Left-shifting a negative value triggers undefined behavior.
Changing the type of value to uint16_t isn't enough. If that's all that is done, the positive value 0xBEEF left-shifted by 16 won't fit into a signed int and again triggers undefined behavior.
An explicit cast to an unsigned type is required here to have well defined behavior. Also note that the bitwise AND is unnecessary since no bits will be stripped off.
uint16_t value = 0xBEEF;
data[i] = ((uint32_t)value << 16) | value;
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