负整数>> 31 = -1不是1?

时间:2013-06-21 00:13:22

标签: c

所以,假设我有一个有符号整数(几个例子):

-1101363339 = 10111110 01011010 10000111 01110101 in binary.
-2147463094 = 10000000 00000000 01010000 01001010 in binary.
-20552      = 11111111 11111111 10101111 10111000 in binary.

现在:-1101363339 >> 31例如,应该等于1吗?但在我的电脑上,我得到-1。无论我选择什么负整数,如果x =负数,x >> 31 = -1。为什么?显然是二进制的,它应该是1。

4 个答案:

答案 0 :(得分:19)

Per C99 6.5.7按位移位运算符:

  

如果E1具有有符号类型和负值,则结果值是实现定义的。

其中E1是班次表达式的左侧。所以它取决于你的编译器你会得到什么。

答案 1 :(得分:10)

在大多数语言中,当您向右移动时,它会进行算术移位,这意味着它会保留最重要的位。因此,在您的情况下,您有二进制的所有1,十进制为-1。如果您使用unsigned int,您将获得所需的结果。

Per C 2011 6.5.7按位移位运算符

  

E1 >> E2的结果是E1右移E2位位置。如果E1无符号类型   或者如果E1具有签名类型和非负值,则结果的值为积分   E1 / 2 E2 的商的一部分。如果E1签名类型和负值,则   结果值是实现定义的。

基本上,负有符号整数的右移是实现定义的,但大多数实现选择将其作为算术移位。

答案 2 :(得分:5)

您所看到的行为称为算术移位,即右移位扩展符号位。这意味着MSB将携带与原始符号位相同的值。换句话说,在左移操作后,负数始终为负。

请注意,此行为是实现定义的,无法使用其他编译器保证。

答案 3 :(得分:2)

您所看到的是算术转换,与您期望的按位转换相反;也就是说,编译器不是“残酷地”移位这些位,而是传播符号位,从而除以2 N


在谈论unsigned int和正int时,右移是一个非常简单的操作 - 位向右移动一个位置(在左边插入0),无论他们的意思。在这种情况下,操作相当于除以2 N (实际上C标准就是这样定义的。)

在谈论负数时会出现这种区别。存在几个负数表示,尽管目前使用最常用的整数2's complement representation

这里“残酷”逐位移位的问题是,对于初学者来说,其中一个位以某种方式用于表示符号;因此,无论负整数表示如何,移动二进制数字都会产生意外结果。

例如,通常在2的表示中,最高有效位对于负数为1,对于正数为0;将按位移位(左边插入零)转换为负数将(在其他情况之间)使其成为正数,而不是导致(通常预期的)除以2 N

因此,引入了算术移位;以2的补码表示的负数有一个有趣的属性:如果不是从左边插入零,而是插入与原始值相同的位,则保留移位的2 N 行为的除法。标志位。

通过这种方式,2 N 的有符号除法可以在移位中使用一些额外的逻辑来执行,而不必采用完全成熟的除法例程。


现在,对于有符号整数,是否保证算术移位?在某些语言中是 1 ,但在C中它不是那样的 - 在处理负整数时移位运算符的行为留作实现定义的细节。

正如经常发生的那样,这是由于对操作的不同硬件支持; C用于完全不同的平台,特别是在过去,根据平台的不同,操作的“成本”也存在很大差异。

例如,如果处理器没有提供算术右移指令,编译器将被强制发出某种更慢的DIV指令,这可能是较慢处理器内部循环中的问题。由于这些原因,C标准将其留给实现者来为当前平台做最合适的事情。

在你的情况下,你的实现可能选择算术移位,因为你在x86处理器上运行,它使用2的补码算法和provides both bitwise and arithmetic shift as single CPU instructions


  1. 实际上,像Java这样的语言甚至分离了算术按位移位运算符 - 这主要是因为它们没有unsigned类型的例子。存储位域。