简体   繁体   English

以零开头的右移

[英]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 ,我会得到这个:

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:这就是 C 和二进制算术的工作方式:

If you left shift 0xff << 3 , you get binary: 00000000 11111111 << 3 = 00000111 11111000如果你左移0xff << 3 ,你得到二进制: 00000000 11111111 << 3 = 00000111 11111000

If you right shift 0xff >> 3 , you get binary: 00000000 11111111 >> 3 = 00000000 00011111如果你右移0xff >> 3 ,你得到二进制: 00000000 11111111 >> 3 = 00000000 00011111

0xff is a (signed) int with the positive value 255 . 0xff是一个(有符号)整数,其值为正值255 Since it is positive, the outcome of shifting it is well-defined behavior in both C and C++.因为它是正数,所以在 C 和 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.也许您正在使用 char 变量或 C++ 字符文字。


Source: ISO 9899:2011 6.5.7.来源: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.假设为二进制补码,则为您提供一个等效于 -1 的负数。

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.它可能会打印 fffffff0 或者它可能会打印一个粉红色的大象,或者它可能会格式化硬盘驱动器。

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.这被称为算术移位,算术右移的工作方式是用移位前的任何位值填充 MSB。 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.在 99% 的现实世界案例中,对有符号数使用按位运算符是没有意义的。 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).因此,始终确保您使用的是无符号数,并且 C/C++ 中没有任何危险的隐式转换规则将它们转换为有符号数(有关危险转换的更多信息,请参阅“整数提升规则”和“通常的算术转换” ”,关于 SO 的大量信息)。

EDIT 2 , some info from the C99 standard's rationale document V5.10:编辑 2 ,来自 C99 标准的基本原理文档 V5.10 的一些信息:

6.5.7 Bitwise shift operators 6.5.7 按位移位运算符

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. K&R 中对移位运算符的描述表明,按 long 计数移位应该强制左操作数在移位之前加宽为 long。 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. C89 委员会认可的一种更直观的做法是,班次计数的类型与结果的类型无关。

QUIET CHANGE IN C89 C89 中的安静变化

Shifting by a long count no longer coerces the shifted operand to long.移位 long 计数不再强制移位的操作数为 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. C89 委员会确认了 K&R 授予的实现自由,不要求签名右移操作进行符号扩展,因为这样的要求可能会减慢快速代码的速度,并且符号扩展移位的用处很小。 (Shifting a negative two's complement integer arithmetically right one place is not the same as dividing by two!) (将负二进制补码整数在算术上右移一位与除以 2 不同!)

If you explicitly shift 0xff it works as you expected如果您明确地将 0xff 移动到您预期的那样

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).只有当0xff的类型为有符号宽度 8 (流行平台上的charsigned char )时,才应该是可能的。


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).右移作为除以 2(如果我理解正确的话,四舍五入)。

So when you have 1 as first bit, you have negative value and after division it's negative again.因此,当您将 1 作为第一位时,您的值为负值,除法后再次为负值

The two kinds of right shift you're talking about are called Logical Shift and Arithmetic Shift .您所说的两种右移称为Logical ShiftArithmetic 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. C 和 C++ 对无符号整数使用逻辑移位,大多数编译器将对有符号整数使用算术移位,但这不是由标准保证的,这意味着右移负有符号 int 的值是实现定义的。

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 .您可以通过将常量替换为0xffU

To explain your real code you just need the C++ versions of the quotes from the C standard that Lundin gave in comments:要解释您的真实代码,您只需要 Lundin 在评论中给出的 C 标准中引用的 C++ 版本:

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

Undefined behavior.未定义的行为。 [expr.shift] says [expr.shift] 说

The value of E1 << E2 is E1 left-shifted E2 bit positions; E1 << E2 的值是 E1 左移的 E2 位位置; 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.如果 E1 具有无符号类型,则结果的值为 E1 × 2 E2 ,比结果类型中可表示的最大值减少模 1。 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;否则,如果 E1 具有有符号类型和非负值,并且 E1×2 E2在结果类型中可表示,则为结果值; 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. E1 >> E2 的值是 E1 右移的 E2 位位置。 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 .如果 E1 具有无符号类型或如果 E1 具有有符号类型和非负值,则结果的值是 E1/2 E2的商的整数部分。 If E1 has a signed type and a negative value, the resulting value is implementation-defined如果 E1 具有有符号类型和负值,则结果值是实现定义的

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!在这里添加我的 5 美分价值......我面临与 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.看起来(在没有任何人有详细知识的情况下)Mac OS X v5.0.1 的 XCode 中的 C 编译器将 MSB 保留为每次移位时都会拉动的进位位。

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?任何人都可以阐明 C 的 Posix4 实现的规范吗? 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.假设我们有一个n位的整数,并且我们希望将前s位清零。

// 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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