Right shift with zeros at the beginning

This is how C and binary arithmetic both work:

If you left shift 0xff << 3, you get binary: 00000000 11111111 << 3 = 00000111 11111000

If you right shift 0xff >> 3, you get binary: 00000000 11111111 >> 3 = 00000000 00011111

0xff is a (signed) int with the positive value 255. Since it is positive, the outcome of shifting it is well-defined behavior in both C and C++. It will not do any arithmetic shifts nor any kind or poorly-defined behavior.

#include <stdio.h>

int main()
{

  printf("%.4X %d\n", 0xff << 3, 0xff << 3);
  printf("%.4X %d\n", 0xff >> 3, 0xff >> 3);

}

Output:

07F8 2040
001F 31

So you are doing something strange in your program because it doesn't work as expected. Perhaps you are using char variables or C++ character literals.


Source: ISO 9899:2011 6.5.7.


EDIT after question update

int number = ~0; gives you a negative number equivalent to -1, assuming two's complement.

number = number << 4; invokes undefined behavior, since you left shift a negative number. The program implements undefined behavior correctly, since it either does something or nothing at all. It may print fffffff0 or it may print a pink elephant, or it may format the hard drive.

number = number >> 4; invokes implementation-defined behavior. In your case, your compiler preserves the sign bit. This is known as arithmetic shift, and arithmetic right shift works in such a way that the MSB is filled with whatever bit value it had before the shift. So if you have a negative number, you will experience that the program is "shifting in ones".

In 99% of all real world cases, it doesn't make sense to use bitwise operators on signed numbers. Therefore, always ensure that you are using unsigned numbers, and that none of the dangerous implicit conversion rules in C/C++ transforms them into signed numbers (for more info about dangerous conversions, see "the integer promotion rules" and "the usual arithmetic conversions", plenty of good info about those on SO).

EDIT 2, some info from the C99 standard's rationale document V5.10:

6.5.7 Bitwise shift operators

The description of shift operators in K&R suggests that shifting by a long count should force the left operand to be widened to long before being shifted. A more intuitive practice, endorsed by the C89 Committee, is that the type of the shift count has no bearing on the type of the result.

QUIET CHANGE IN C89

Shifting by a long count no longer coerces the shifted operand to long. The C89 Committee affirmed the freedom in implementation granted by K&R in not requiring the signed right shift operation to sign extend, since such a requirement might slow down fast code and since the usefulness of sign extended shifts is marginal. (Shifting a negative two’s complement integer arithmetically right one place is not the same as dividing by two!)


If you explicitly shift 0xff it works as you expected

cout << (0xff >> 3) << endl; // 31

It should be possible only if 0xff is in type of signed width 8 (char and signed char on popular platforms).


So, in common case:

You need to use unsigned ints

(unsigned type)0xff

right shift works as division by 2(with rounding down, if I understand correctly).

So when you have 1 as first bit, you have negative value and after division it's negative again.


The two kinds of right shift you're talking about are called Logical Shift and Arithmetic Shift. C and C++ use logical shift for unsigned integers and most compilers will use arithmetic shift for a signed integer but this is not guaranteed by the standard meaning that the value of right shifting a negative signed int is implementation defined.

Since you want a logical shift you need to switch to using an unsigned integer. You can do this by replacing your constant with 0xffU.