简体   繁体   中英

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? ie 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?

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 .

To explain your real code you just need the C++ versions of the quotes from the C standard that Lundin gave in comments:

int number = ~0;
number = number << 4;

Undefined behavior. [expr.shift] says

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

number = ~0;
number = number >> 4;

Implementation-defined result, in this case your implementation gave you an arithmetic shift:

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 non-negative value, the value of the result is the integral part of the quotient of E1/2 E2 . If E1 has a signed type and a negative value, the resulting value is implementation-defined

You should use an unsigned type:

unsigned int number = -1;
number = number >> 4;
std::cout << std::hex << number << std::endl;

Output:

0x0fffffff

To add my 5 cents worth here... I'm facing exactly the same problem as this.lau! I've done some perfunctory research on this and these are my results:

typedef unsigned int Uint;
#define U31 0x7FFFFFFF
#define U32 0xFFFFFFFF

printf ("U31 right shifted: 0x%08x\n", (U31 >> 30));
printf ("U32 right shifted: 0x%08x\n", (U32 >> 30));

Output:
U31 right shifted: 0x00000001 (expected)
U32 right shifted: 0xffffffff (not expected)

It would appear (in the absence of anyone with detailed knowledge) that the C compiler in XCode for Mac OS X v5.0.1 reserves the MSB as a carry bit that gets pulled along with each shift.

Somewhat annoyingly, the converse is NOT true:-

#define ST00 0x00000001
#define ST01 0x00000002

printf ("ST00 left shifted: 0x%08x\n", (ST00 << 30));
printf ("ST01 left shifted: 0x%08x\n", (ST01 << 30));

Output:
ST00 left shifted: 0x40000000
ST01 left shifted: 0x80000000

I concur completely with the people above that assert that the sign of the operand has no bearing on the behaviour of the shift operator.

Can anyone shed any light on the specification for the Posix4 implementation of C? I feel a definitive answer may rest there.

In the meantime, it appears that the only workaround is a construct along the following lines;-

#define CARD2UNIVERSE(c) (((c) == 32) ? 0xFFFFFFFF : (U31 >> (31 - (c))))

This works - exasperating but necessary.

以防万一,如果您希望右移后负数的第一位为 0,我们可以做的是将该负数与 INT_MIN 进行异或,这将使其 msb 为零,我知道这不是适当的算术移位,但会完成工作

Even though there are several answers, I will also add a version that is agnostic to the number of bits in the integer as well as it's signed-ness.

Let's say we have an integer of n bits, and we want to have the first s bits zeroed.

// this could be any type of integer including long long
// e.g. typedef unsigned int MyInt;
typedef long long MyInt;

size_t n = sizeof(MyInt);
size_t s = 5; // say we want the first five bits set to zero

// get the most significant bit set to 1
#define MSB1 (((MyInt)1) << (n - 1))  // MSB1 = 10000000....00

// flip the bits
#define MSB0 (~MSB1)                  // MSB0 = 01111111....11

// Now shift right. The bits will fill with zero regardless of
// whether MyInt is signed or unsigned, because we have a leading 0,
// and the integer is not negative regardless of the signed-ness.
// but we will have to shift right 1 time less.
#define MASK (MSB0 >> (s - 1))        // MASK = 00000111....11

Alternatively,

#define MASK ((~(((MyInt)1) << (n - 1))) >> (s - 1))
// Still, MASK = 00000111....11

If you want to shift right any arbitrary integer with zeros first, then you could,

// Shift right once
MyInt x = 0xff..ff0;
MyInt result = x >> 1; // 0xff..ff8;

// Bitwise and with `MSB0`, that is `0111...11`, thereby flipping the MSB to 0.
result &= MSB0; // 0x7f..ff8

Shift right the rest of times.
result >>= (s - 1) // 0x0f..fff

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM