5

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?

geohei
  • 696
  • 4
  • 15
  • 1
    But you aren't simply shifting. You are also doing addition. – Emanuel P Apr 20 '21 at 15:59
  • I think `0xFF` forces the compiler to cast your value to `int64_t`. Am I wrong? https://godbolt.org/z/88T16Y5G5 –  Apr 20 '21 at 16:00
  • @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?](https://stackoverflow.com/q/46246411/995714), [bit shifting with unsigned long type produces wrong results](https://stackoverflow.com/q/31744305/995714) – phuclv Apr 21 '21 at 00:31

1 Answers1

3

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.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • I think it is implementation defined behavior? – Eugene Sh. Apr 20 '21 at 16:01
  • 1
    @EugeneSh. It's undefined as per 6.5.7p4. Right shifting a negative value is implementation defined. – dbush Apr 20 '21 at 16:03
  • Ah, my bad. I used to trust my memory once :) – Eugene Sh. Apr 20 '21 at 16:04
  • @dbush Uah ... perfect answer. That explains it all. Many thanks! – geohei Apr 20 '21 at 16:15
  • @dbush Assuming ```0xFF``` would be an ```unsigned char``` instead of ```unsigned int```, would 6.5.7p4 also apply? So replace ```0xFF``` above by ```c``` defined as ```unsigned char c = 0xFF```. This doesn't work, despite it's unsigned. – geohei Apr 20 '21 at 16:27
  • @geohei That's because the `unsigned char` value is first *promoted* to type `int` before being used in a expression. – dbush Apr 20 '21 at 16:28