右移C中的负数

Alp*_*neo 26 c bit-shift negative-number

我有C代码,我在其中执行以下操作.

int nPosVal = +0xFFFF;   // + Added for ease of understanding
int nNegVal = -0xFFFF;   // - Added for valid reason
Run Code Online (Sandbox Code Playgroud)

现在,当我尝试

printf ("%d %d", nPosVal >> 1, nNegVal >> 1);
Run Code Online (Sandbox Code Playgroud)

我明白了

32767 -32768
Run Code Online (Sandbox Code Playgroud)

这是预期的吗?

我能够想到类似的东西

65535 >> 1 = (int) 32767.5 = 32767
-65535 >> 1 = (int) -32767.5 = -32768
Run Code Online (Sandbox Code Playgroud)

也就是说,-32767.5四舍五入为-32768.

这种理解是否正确?

Boo*_*jum 40

看起来你的实现可能正在用两个补码数进行算术位移.在这个系统中,它将所有位向右移动,然后用最后一位的副本填充高位.所以对于你的例子,在这里将int视为32位:

nPosVal = 00000000000000001111111111111111
nNegVal = 11111111111111110000000000000001
Run Code Online (Sandbox Code Playgroud)

转变后,你有:

nPosVal = 00000000000000000111111111111111
nNegVal = 11111111111111111000000000000000
Run Code Online (Sandbox Code Playgroud)

如果将其转换回十进制,则分别得到32767和-32768.

实际上,右移向负无穷大转向.

编辑: 根据最新标准草案的 6.5.7节,负数的这种行为取决于实现:

E1 >> E2的结果是E1右移E2位位置.如果E1具有无符号类型或者E1具有有符号类型和非负值,则结果的值是E1/2 E2的商的整数部分.如果E1具有带符号类型和负值,则结果值是实现定义的.

他们声明的理由:

C89委员会肯定了K&R授予的实施自由,不要求签署权利转移操作签署延期,因为这样的要求可能会减慢快速代码的速度,并且因为符号扩展班次的有用性是微不足道的.(在一个地方算术右移一个负2的补数整数与除以2的一样!)

所以它的实现依赖于理论.在实践中,我从未见过一个实现在左操作数被签名时没有进行算术移位.

  • 它依赖于实现。(请参阅上面我的编辑。)正如我所说,我从未见过实现在这方面有所不同,但理论上可以。 (3认同)

Mar*_*ers 18

不,在使用整数时,您不会得到像0.5这样的小数.当您查看两个数字的二进制表示时,可以很容易地解释结果:

      65535: 00000000000000001111111111111111
     -65535: 11111111111111110000000000000001
Run Code Online (Sandbox Code Playgroud)

位移到右边一位,并在左边延伸(请注意,这取决于实现,感谢Trent):

 65535 >> 1: 00000000000000000111111111111111
-65535 >> 1: 11111111111111111000000000000000
Run Code Online (Sandbox Code Playgroud)

转换回十进制:

 65535 >> 1 = 32767
-65535 >> 1 = -32768
Run Code Online (Sandbox Code Playgroud)

  • 请注意,左侧的扩展是依赖于实现的. (6认同)
  • 标准说:"如果右操作数的值为负或大于或等于提升的左操作数的宽度,则行为未定义." (2认同)
  • @Gonzalo:标准确实说了这一点,但是在这种情况下,正确的操作数是“ 1”。这不是负数,也不是> = int的宽度。 (2认同)

Tre*_*ent 7

C规范没有规定符号位是否被移位.它取决于实现.