我试图弄清楚算术位移运算符在C中是如何工作的,以及它将如何影响带符号的32位整数。
为简单起见,假设我们在一个字节(8位)内工作:
x = 1101.0101
MSB[ 1101.0101 ]LSB
阅读Stack Overflow和一些网站上的其他帖子,我发现:
<<
将转向MSB(在我的情况下向左移动),并用0填充“空”LSB位。
>>
将转向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 !!!!!)
为什么?
答案 0 :(得分:56)
负签名号的右移具有实现定义的行为。
如果您的8位用于表示带符号的8位值(因为您在切换到8位示例之前所说的是“带符号的32位整数”),那么您的数字为负数。向右移动可以用原始MSB填充“空”位(即执行符号扩展),也可以用零移位,具体取决于平台和/或编译器。
(实现定义的行为意味着编译器会做一些合理的事情,但是以平台相关的方式;编译器文档应该告诉你什么。)
左移,如果数字开始为负,或者移位操作将1移至符号位或超出符号位,则具有未定义的行为(对于导致溢出的有符号值的大多数操作也是如此)。 p>
(未定义的行为意味着任何事情都可能发生。)
无符号值的相同操作在两种情况下都是明确定义的:“空”位将用0填充。
答案 1 :(得分:35)
没有为负值定义按位移位操作
代表'&lt;&lt;&lt;'
6.5.7 / 4 [...]如果E1具有有符号类型和非负值,并且E1×2 E2 在结果类型中可表示,那么这就是结果值;否则,行为未定义。
和'&gt;&gt;'
6.5.7 / 5 [...]如果E1具有有符号类型和负值,则结果值是实现定义的。
在特定实现上研究这些操作对已签名数字的行为是浪费时间,因为您无法保证它在任何其他实现上的工作方式相同(例如,您的编译器就是具有特定commad-line参数的计算机。)
它可能甚至不适用于相同编译器的较旧版本或较新版本。编译器甚至可能将这些位定义为随机或未定义。这意味着,在源代码中使用完全相同的代码序列可能会产生完全不同的结果,甚至依赖于汇编优化或其他寄存器使用等事情。如果封装在一个函数中,它可能甚至不会在具有相同参数的两个连续调用中的那些位中产生相同的结果。
仅考虑非负值,左移1(expression << 1
)的效果与将表达式多表达2相同(假设表达式* 2不溢出)右移1(expression >> 1
)的效果与除以2相同。
答案 2 :(得分:5)
正如其他人所说,负值的转移是实施定义的。
大多数实现通过使用符号位填充移位来将带符号的右移作为floor(x / 2 N )处理。这在实践中非常方便,因为这种操作非常普遍。另一方面,如果您将右移无符号整数,则位移位将归零。
从机器方面来看,大多数实现都有两种类型的右移指令:
“算术”右移(通常具有助记符ASR或SRA),如我所解释的那样。
一个'逻辑'右移(具有助记符LSR或SRL或SR),可以按预期工作。
大多数编译器首先用于签名类型,第二个用于无符号类型。为方便起见。
答案 3 :(得分:0)
在32位编译器中
x = x&gt;&gt; 31;
这里x是有符号整数,所以第32位是符号位。
最终x值为100000 ... 000 。和第32位表示-ive值。
这里x值实现1&#39; s赞美。
然后最终x是-32768
答案 4 :(得分:0)
从c ++ 20起,已定义了有符号整数的按位移位运算符。
左移/**
*
* @OA\Get(
* path="/oauth2/authorize",
* @OA\Response(response="200", description="An access token"),
* tags={"auth"},
* @OA\Parameter(
* name="response_type",
* in="query",
* type="string",
* description="the type of response",
* required=true,
* default="code"
* ),
* @OA\Parameter(
* name="client_id",
* in="query",
* type="string",
* description="the client identifier",
* required=true,
* default="testclient"
* ),
* @OA\Parameter(
* name="client_secret",
* in="query",
* type="string",
* description="the client identifier",
* required=false,
* default="testclient"
* ),
* @OA\Parameter(
* name="redirect_uri",
* in="query",
* type="string",
* description="where to send the response",
* required=false
* ),
* @OA\Parameter(
* name="state",
* in="query",
* type="string",
* description="with a CSRF token. This parameter is optional but highly recommended.",
* required=false,
* ),
* @OA\Parameter(
* name="scope",
* in="query",
* type="string",
* description="allowed scopes, space separated",
* required=false,
* )
* )
*/
public function authorizeAction()
{
// code
}
等效于a<<b
模量a*2^b
,其中2^N
是结果类型中的位数。特别是N
实际上是最小的1<<31
值。
右移int
等效于a>>b
,向下舍入(即朝负无穷大)。所以例如a/2^b
。
有关更多详细信息,请参见https://en.cppreference.com/w/cpp/language/operator_arithmetic。
(有关较旧的标准,请参见Matthew Slattery的答案)
答案 5 :(得分:0)
在我的i7上:
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
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
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