C语言 有符号整数的算术位移
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4009885/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Arithmetic bit-shift on a signed integer
提问by newprint
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 << 7will 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. This would result in [0000.0010]? Am I right?
现在,让我们说我会>> 7,最后的结果。这会导致[0000.0010]? 我对吗?
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?
为什么?
回答by Matthew Slattery
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. Shifting it right may fill "empty" bits with the original MSB (i.e. perform sign extension) or it may shift in zeroes, depending on platform and/or compiler.
如果您的 8 位旨在表示有符号的 8 位值(在切换到 8 位示例之前,您正在谈论“有符号的 32 位整数”),那么您将得到一个负数。右移可能会用原始 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 unsignedvalues are well-defined in both cases: the "empty" bits will be filled with 0.
在这两种情况下,对无符号值的相同操作都有明确定义:“空”位将填充为 0。
回答by pmg
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×2E2is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.
6.5.7/4 [...] 如果 E1 有符号类型和非负值,并且 E1×2 E2在结果类型中是可表示的,那么这就是结果值;否则,行为未定义。
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.
回答by Vovanium
As others said shift of negative value is implementation-defined.
正如其他人所说,负值的转变是实现定义的。
Most of implementations treat signed right shift as floor(x/2N) by filling shifted in bits using sign bit. 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.
大多数实现通过使用符号位填充位移位来将有符号右移视为 floor(x/2 N)。在实践中非常方便,因为这种操作很常见。另一方面,如果您将右移无符号整数,则移位的位将为零。
Looking from machine side, most implementations have two types of shift-right instructions:
从机器方面来看,大多数实现都有两种类型的右移指令:
An 'arithmetic' shift right (often having mnemonic ASR or SRA) which works as me explained.
A 'logic' shift right (oftem having mnemonic LSR or SRL or SR) which works as you expect.
一个“算术”右移(通常有助记符 ASR 或 SRA),就像我解释的那样工作。
“逻辑”右移(通常具有助记符 LSR 或 SRL 或 SR),它按您的预期工作。
Most of compilers utilize first for signed types and second for unsigned ones. Just for convenience.
大多数编译器首先对有符号类型使用,然后对无符号类型使用。只是为了方便。
回答by example
As of c++20the bitwise shift operators for signed integers are well defined.
从 c++20 开始,有符号整数的按位移位运算符定义良好。
The left shift a<<bis equivalent to a*2^bmodulus 2^Nwhere Nis the number of bits in the resulting type. In particular 1<<31is in fact the smallest intvalue.
左移a<<b等效于a*2^b模数2^N,其中N是结果类型中的位数。特别1<<31是实际上是最小值int。
The right shift a>>bis equivalent to a/2^b, rounded down (ie. towards negative infinity). So e.g. -1>>10 == -1.
右移a>>b相当于a/2^b,向下取整(即朝负无穷大)。所以例如-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 的答案)
回答by Andreas Haferburg
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
回答by user3060837
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. and 32nd bit indicate -ive value.
最终 x 值为 100000...000。第 32 位表示 -ive 值。
here x value implement to 1's compliment.
这里 x 值实现为 1 的恭维。
then final x is -32768
那么最终的 x 是 -32768

