5
votes

Please have a look at the following code snippet, which basically simply bit shifts 1 byte by 24 bits to the left.

uint64_t i = 0xFFFFFFFF00000000;
printf("before: %016llX \n", i);
i += 0xFF << 24;
printf("after : %016llX \n", i);

// gives:
// before: FFFFFFFF00000000
// after : FFFFFFFEFF000000

The most significant 32 bits are FFFFFFFE (watch the E at the end). This is not as I expected. I don't see why shifting 1 bytes 24 bits left would touch bit #32 (bit #31 should be the last one modified) It changed the last F (1111) into E (1110)).

To make it work properly, I had use 0xFF unsigned (0xFFU).

uint64_t i = 0xFFFFFFFF00000000;
printf("before: %016llX \n", i);
i += 0xFFU << 24;
printf("after : %016llX \n", i);

// gives:
// before: FFFFFFFF00000000
// after : FFFFFFFFFF000000

Why does the bit shift with a signed int (0xFF) touch/reset one bit too much?

1
But you aren't simply shifting. You are also doing addition. - Emanuel P
I think 0xFF forces the compiler to cast your value to int64_t. Am I wrong? godbolt.org/z/88T16Y5G5 - user12722843
@Hrant no, there's no cast in 0xFF << 24. The entire shift was done in int precision, then the result is cast to uint64_t Why does shifting 0xff left by 24 bits result in an incorrect value?, bit shifting with unsigned long type produces wrong results - phuclv

1 Answers

3
votes

You left-shifted into the sign bit.

The integer constant 0xFF has type int. Assuming an int is 32 bit, the expression 0xFF << 24 shifts a bit set to 1 into the high-order bit of a signed integer triggers undefined behavior which in your case manifested as an unexpected value.

This is spelled out in section 6.5.7p4 of the C standard:

The result of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are filled with zeros. If E1 has an unsigned type, the value of the result is E1×2E2, reduced modulo one more than the maximum value representable in the result type. If E1 has a signed type and nonnegative value, and E1×2E2is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.

By using the U suffix this makes the constant have type unsigned int, and it is valid to shift bits set to 1 into the high-order bit because there is no sign bit.