C++ – Is left-shifting a signed integer undefined behavior in C++03

clanguage-lawyershift

According to C++03, 5.8/2, left-shifting is defined as follows:

The value of E1 << E2 is E1 (interpreted as a bit pattern) left-shifted E2 bit positions; vacated bits are zero-filled. If E1 has an unsigned type, the value of the result is E1 multiplied by the quantity 2 raised to the power E2, reduced modulo ULONG_MAX+1 if E1 has type unsigned long, UINT_MAX+1 otherwise.

What bothers me here is that unsigned types are explicitly mentioned yet signed types are ignored completely. Compare this to 5.8/3 which defines right-shifting:

The value of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 divided by the quantity 2 raised to the power E2. If E1 has a signed type and a negative value, the resulting value is implementation-defined.

In 5.8/3 both signed and unsigned are explicitly mentioned, even signed holding non-negative and signed holding negative values are mentioned separately.

AFAIK when something is not explicitly defined in C++ Standard the behavior is undefined. I've also seen this question, but it focuses on differences between C and C++ and doesn't seem to have an answer everyone would agree upon.

Is left-shifting a signed integer defined in C++03?

Best Answer

5.8/2 says that it interprets it as a bit-pattern, which is only implementation dependent if for some reason your implementation doesn't use 2's complement, or if your compiler second-guesses you (they don't). C++11 is more explicit, but says the same thing.

Signed integers use what's known as 2's complement. Basically if you bit-shift a signed integer by 1, if it's positive and below 2^(bits - 2) it will work as if it were unsigned. If it is above that but positive, you will create a strange negative number that bears no relation to the original. If it is negative to begin with, you'll get possibly a negative, possibly a positive number.

For instance, if we have an 8-bit signed integer representing -1:

11111111 // -1

If we left-shift that we end up with

11111110 // -2

However, let's say we have -120

10001000  // -120

We shall end up with

00010000  // 16

Obviously that is not correct!

Continuing, using number 65:

01000001  // 65

Shifted left, this will become:

10000001  // -127

Which equates to -127.

However, the number 16:

00010000 // 16

Shifted left is

00100000 // 32

As you can see, it "sometimes works, sometimes doesn't" but usually works if your number is below 2^(bits-2) and sometimes but not usually if it is above -(2^(bits-2)). That is, to shift left by 1. To shift left by 2, take another bit off. Etc.

Related Topic