Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Right shift with zeros at the beginning

I'm trying to do a kind of left shift that would add zeros at the beginning instead of ones. For example, if I left shift 0xff, I get this:

0xff << 3 = 11111000

However, if I right shift it, I get this:

0xff >> 3 = 11111111

Is there any operation I could use to get the equivalent of a left shift? i.e. I would like to get this:

00011111

Any suggestion?

Edit

To answer the comments, here is the code I'm using:

int number = ~0;
number = number << 4;   
std::cout << std::hex << number << std::endl;

number = ~0;
number = number >> 4;
std::cout << std::hex << number << std::endl;

output:

fffffff0
ffffffff

Since it seems that in general it should work, I'm interested as to why this specific code doesn't. Any idea?

like image 617
laurent Avatar asked Jan 18 '13 10:01

laurent


1 Answers

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!)

like image 197
Lundin Avatar answered Sep 22 '22 06:09

Lundin