我试图弄清楚算术位移运算符在C中是如何工作的,以及它将如何影响带符号的32位整数。
为简单起见,假设我们在一个字节(8位)内工作:
x = 1101.0101
MSB[ 1101.0101 ]LSB
阅读Stack Overflow和一些网站上的其他帖子,我发现:
<
并且>>将向LSB移动(在我的情况下向右移动)并用MS位填充"空"位
因此,x = x << 7将导致将LSB移至MSB,并将所有内容设置为0。
1000.0000
现在,让我说我会>> 7,最后的结果。 这会导致[0000.0010]? 我对吗?
关于转移运营商我的假设是对的吗?
我刚在我的机器上测试过,**
int x = 1; //000000000......01
x = x << 31; //100000000......00
x = x >> 31; //111111111......11 (Everything is filled with 1s !!!!!)
为什么?
C中Shift运算符的可能重复
"为什么?" 因为您的实施是这样说的。 检查文档:它必须定义右移负值的行为(或者您没有C编译器)。
@ pseudonym27,你必须在文档中找到
@ pseudonym27:右移很明确,左移不是。 考虑左移位作为连续乘法乘以2。 如果任何乘法溢出,则表示未定义行为。
C11标准草案(PDF文件)可在线免费获取。
超集:stackoverflow.com/questions/11644362/
负签名号的右移具有实现定义的行为。
如果您的8位用于表示带符号的8位值(正如您在切换到8位示例之前所说的那样是"带符号的32位整数"),那么您的数字为负数。向右移位可以用原始MSB填充"空"位(即执行符号扩展),或者它可以在零中移位,这取决于平台和/或编译器。
(实现定义的行为意味着编译器会做一些合理的事情,但是以平台相关的方式;编译器文档应该告诉你什么。)
左移,如果数字开始为负,或者移位操作将1移位到符号位或超出符号位,则具有未定义的行为(对于导致溢出的有符号值的大多数操作也是如此)。
(未定义的行为意味着任何事情都可能发生。)
在两种情况下,对无符号值的相同操作都是明确定义的:"空"位将用0填充。
应该说,标准的,可移植的语义的右移也被称为两个幂的字面值。大多数编译器将为这些分区生成算术移位,从而产生快速代码,根据需要进行符号扩展。
@KubaOber:现代标准要求x/2在x为负奇数的情况下与右移不同。正常的算术右移将执行浮动除法(因此在不溢出(n+d)/d == (n/d)+1的情况下,但需要除法使用截断除法,从而防止编译器使用简单的移位。
@KubaOber:应该注意的是,有符号值的左移曾经是两次幂的乘法,但是较新的编译器似乎已经弃用了对负数左移的支持。
表示在超现代编译器上,"任何事物"包括重新排列程序逻辑以假装负值是正的可能是好的。鉴于void foo(int x) { if (x >= 0) printf("Kaboom!"); int y= x<<4; }调用foo(-1);很可能会打印"Kaboom!"。
没有为负值定义按位移位操作
为'<6.5.7/4 [...] If E1 has a signed type and nonnegative value, and E1×2E2 is representable in the result type, then that is the resulting value; otherwise, the behavior is undefined.
和'>>'
6.5.7/5 [...] If E1 has a signed type and a negative value, the resulting value is implementation- defined.
在特定实现上研究这些操作在签名数字上的行为是浪费时间,因为您无法保证它在任何其他实现上的工作方式相同(例如,您在计算机上使用您的编译器进行编译)特定的commad-line参数)。
它甚至可能不适用于相同编译器的较旧版本或较新版本。编译器甚至可能将这些位定义为随机或未定义。这意味着,在源代码中使用完全相同的代码序列可能会产生完全不同的结果,甚至依赖于汇编优化或其他寄存器使用等事情。如果封装在一个函数中,它可能甚至不会在具有相同参数的两个连续调用中的那些位中产生相同的结果。
仅考虑非负值,左移1(expression << 1)的效果与将表达式乘以2(假设表达式* 2不溢出)和右移1的效果相同(expression >> 1)除以2相同。
正如其他人所说,负值的转移是实施定义的。
大多数实现通过使用符号位填充移位来将带符号的右移作为floor(x / 2N)处理。这在实践中非常方便,因为这种操作非常普遍。另一方面,如果您将右移无符号整数,则位移位将归零。
从机器方面来看,大多数实现都有两种类型的右移指令:
一个'算术'右移(通常有助记符ASR或SRA),就像我解释的那样。
"逻辑"右移(具有助记符LSR或SRL或SR)可以按预期工作。
大多数编译器首先用于签名类型,第二个用于无符号类型。只是为了方便。
从c ++ 20开始,对有符号整数的按位移位运算符进行了很好的定义。
左移a
右移a>>b等于a/2^b,向下舍入(即向负无穷大)。所以例如-1>>10 == -1。
有关更多详细信息,请参阅https://en.cppreference.com/w/cpp/language/operator_arithmetic。
(对于较旧的标准,请参阅Matthew Slattery的答案)
在32位编译器中
x = x >> 31;
这里x是有符号整数,所以第32位是符号位。
最终x值为100000 ... 000。和第32位表示-ive值。
这里x值实现1的赞美。
然后最后的x是-32768