简体   繁体   English

有符号整数的算术位移

[英]Arithmetic bit-shift on a signed integer

I am trying to figure out how exactly arithmetic bit-shift operators work in C, and how it will affect signed 32-bit integers.我试图弄清楚算术位移运算符在 C 中的工作原理,以及它将如何影响有符号的 32 位整数。

To make things simple, let's say we work within one byte (8 bits):为简单起见,假设我们在一个字节(8 位)内工作:

x = 1101.0101
MSB[ 1101.0101 ]LSB

Reading other posts on Stack Overflow and some websites, I found that: << will shift toward MSB (to the left, in my case), and fill "empty" LSB bits with 0s.阅读 Stack Overflow 和一些网站上的其他帖子,我发现: <<将移向 MSB(在我的情况下向左),并用 0 填充“空”LSB 位。

And >> will shift toward LSB (to the right, in my case) and fill "empty" bits with MS bit并且>>将移向 LSB(在我的情况下向右)并用 MS 位填充“空”位

So, x = x << 7 will result in moving LSB to MSB, and setting everything to 0s.因此, x = x << 7将导致将 LSB 移动到 MSB,并将所有内容设置为 0。

1000.0000

Now, let's say I would >> 7 , last result.现在,假设我会>> 7 ,最后一个结果。 This would result in [0000.0010] ?这将导致[0000.0010] Am I right?我说得对吗?

Am I right about my assumptions about shift operators?我对移位运算符的假设是否正确?

I just tested on my machine, **我刚刚在我的机器上测试过,**

int x = 1;   //000000000......01

x = x << 31; //100000000......00

x = x >> 31; //111111111......11 (Everything is filled with 1s !!!!!) 

Why?为什么?

Right shift of a negative signed number has implementation-defined behaviour.负符号数的右移具有实现定义的行为。

If your 8 bits are meant to represent a signed 8 bit value (as you're talking about a "signed 32 bit integer" before switching to 8 bit examples) then you have a negative number.如果您的 8 位旨在表示有符号的 8 位值(在切换到 8 位示例之前,您正在谈论“有符号的 32 位整数”),那么您将得到一个负数。 Shifting it right may fill "empty" bits with the original MSB (ie perform sign extension) or it may shift in zeroes, depending on platform and/or compiler.右移可能会用原始 MSB 填充“空”位(即执行符号扩展),也可能会移位为零,具体取决于平台和/或编译器。

(Implementation-defined behaviour means that the compiler will do something sensible, but in a platform-dependent manner; the compiler documentation is supposed to tell you what.) (实现定义的行为意味着编译器会做一些明智的事情,但是以平台相关的方式;编译器文档应该告诉你什么。)


A left shift, if the number either starts out negative, or the shift operation would shift a 1 either to or beyond the sign bit, has undefined behaviour (as do most operations on signed values which cause an overflow).如果数字以负数开头,或者移位操作会将 1 移到符号位或超出符号位,则左移具有未定义的行为(与导致溢出的对有符号值的大多数操作一样)。

(Undefined behaviour means that anything at all could happen.) (未定义的行为意味着任何事情都可能发生。)


The same operations on unsigned values are well-defined in both cases: the "empty" bits will be filled with 0.在这两种情况下,对无符号值的相同操作都有明确定义:“空”位将填充为 0。

Bitwise shift operations are not defined for negative values没有为负值定义按位移位操作

for '<<'对于“<<”

6.5.7/4 [...] If E1 has a signed type and nonnegative value, and E1×2 E2 is representable in the result type, then that is the resulting value; 6.5.7/4 [...] 如果 E1 有符号类型和非负值,并且 E1×2 E2在结果类型中是可表示的,那么这就是结果值; otherwise, the behavior is undefined.否则,行为未定义。

and for '>>'而对于“>>”

6.5.7/5 [...] If E1 has a signed type and a negative value, the resulting value is implementation- defined. 6.5.7/5 [...] 如果 E1 具有有符号类型和负值,则结果值是实现定义的。

It's a waste of time to study the behaviour of these operations on signed numbers on a specific implementation, because you have no guarantee it will work the same way on any other implementation (an implementation is, for example, you compiler on your computer with your specific commad-line parameters).在特定实现上研究这些对有符号数的操作的行为是浪费时间,因为您无法保证它会在任何其他实现上以相同的方式工作(例如,实现是您在计算机上编译特定的命令行参数)。

It might not even work for an older or a newer version of the very same compiler.它甚至可能不适用于同一个编译器的较旧或较新版本。 The compiler might even define those bits as random or undefined.编译器甚至可能将这些位定义为随机或未定义。 This would mean that the very same code sequence could produce totally different results when used across your sources or even depend on things like assembly optimisation or other register usage.这意味着相同的代码序列在跨源使用时可能会产生完全不同的结果,甚至取决于诸如汇编优化或其他寄存器使用之类的事情。 If encapsulated in a function it might not even produce the same result in those bits on two consecutive calls with the same arguments.如果封装在一个函数中,它甚至可能不会在具有相同参数的两个连续调用中的那些位中产生相同的结果。

Considering only non-negative values , the effect of left shifting by 1 ( expression << 1 ) is the same as multpliying the expression by 2 (provided expression * 2 does not overflow) and the effect of right shifting by 1 ( expression >> 1 ) is the same as dividing by 2.仅考虑非负值,左移 1( expression << 1 )的效果与将表达式乘以 2(提供表达式 * 2 不溢出)和右移 1 的效果相同( expression >> 1 ) 与除以 2 相同。

As of c++20 the bitwise shift operators for signed integers are well defined.从 c++20 开始,有符号整数的按位移位运算符定义良好。

The left shift a<<b is equivalent to a*2^b modulus 2^N where N is the number of bits in the resulting type.左移a<<b等效于a*2^b模数2^N ,其中N是结果类型中的位数。 In particular 1<<31 is in fact the smallest int value.特别是1<<31实际上是最小的int值。

The right shift a>>b is equivalent to a/2^b , rounded down (ie. towards negative infinity).右移a>>b相当于a/2^b ,向下舍入(即朝负无穷大)。 So eg -1>>10 == -1 .所以例如-1>>10 == -1

For some more details see https://en.cppreference.com/w/cpp/language/operator_arithmetic .有关更多详细信息,请参阅https://en.cppreference.com/w/cpp/language/operator_arithmetic

(for the older standards see the answer by Matthew Slattery) (对于较旧的标准,请参阅 Matthew Slattery 的答案)

As others said shift of negative value is implementation-defined.正如其他人所说,负值的转变是实现定义的。

Most of implementations treat signed right shift as floor(x/2 N ) by filling shifted in bits using sign bit.大多数实现通过使用符号位填充位移位来将有符号右移视为 floor(x/2 N )。 It is very convenient in practice, as this operation is so common.在实践中非常方便,因为这种操作很常见。 On the other hand if you will shift right unsigned integer, shifted in bits will be zeroed.另一方面,如果您将右移无符号整数,则移位的位将为零。

Looking from machine side, most implementations have two types of shift-right instructions:从机器方面来看,大多数实现都有两种类型的右移指令:

  1. An 'arithmetic' shift right (often having mnemonic ASR or SRA) which works as me explained.一个“算术”右移(通常有助记符 ASR 或 SRA),就像我解释的那样工作。

  2. A 'logic' shift right (oftem having mnemonic LSR or SRL or SR) which works as you expect. “逻辑”右移(通常具有助记符 LSR 或 SRL 或 SR),它按您的预期工作。

Most of compilers utilize first for signed types and second for unsigned ones.大多数编译器首先对有符号类型使用,然后对无符号类型使用。 Just for convenience.只是为了方便。

In the 32 bit compiler在 32 位编译器中

x = x >> 31; x = x >> 31;

here x is the signed integer so 32nd bit is sign bit.这里 x 是有符号整数,所以第 32 位是符​​号位。

final x value is 100000...000 .最终 x 值为 100000...000 and 32nd bit indicate -ive value.第 32 位表示 -ive 值。

here x value implement to 1's compliment.这里 x 值实现为 1 的恭维。

then final x is -32768那么最终的 x 是 -32768

On my i7:在我的 i7 上:

uint64_t: uint64_t:

0xffffffffffffffff >> 0 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 1 is 0b0111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 2 is 0b0011111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 3 is 0b0001111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 4 is 0b0000111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 62 is 0b0000000000000000000000000000000000000000000000000000000000000011
0xffffffffffffffff >> 63 is 0b0000000000000000000000000000000000000000000000000000000000000001
0xffffffffffffffff >> 64 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 65 is 0b0111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 66 is 0b0011111111111111111111111111111111111111111111111111111111111111

int64_t -1 int64_t -1

0xffffffffffffffff >> 0 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 1 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 2 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 3 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 4 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 62 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 63 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 64 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 65 is 0b1111111111111111111111111111111111111111111111111111111111111111
0xffffffffffffffff >> 66 is 0b1111111111111111111111111111111111111111111111111111111111111111

int64_t 2^63-1 int64_t 2^63-1

0x7fffffffffffffff >> 0 is 0b0111111111111111111111111111111111111111111111111111111111111111
0x7fffffffffffffff >> 1 is 0b0011111111111111111111111111111111111111111111111111111111111111
0x7fffffffffffffff >> 2 is 0b0001111111111111111111111111111111111111111111111111111111111111
0x7fffffffffffffff >> 3 is 0b0000111111111111111111111111111111111111111111111111111111111111
0x7fffffffffffffff >> 4 is 0b0000011111111111111111111111111111111111111111111111111111111111
0x7fffffffffffffff >> 62 is 0b0000000000000000000000000000000000000000000000000000000000000001
0x7fffffffffffffff >> 63 is 0b0000000000000000000000000000000000000000000000000000000000000000
0x7fffffffffffffff >> 64 is 0b0111111111111111111111111111111111111111111111111111111111111111
0x7fffffffffffffff >> 65 is 0b0011111111111111111111111111111111111111111111111111111111111111
0x7fffffffffffffff >> 66 is 0b0001111111111111111111111111111111111111111111111111111111111111

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

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