右移C中的负数

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

int nPosVal = +0xFFFF; // + Added for ease of understanding int nNegVal = -0xFFFF; // - Added for valid reason 

现在,当我尝试

 printf ("%d %d", nPosVal >> 1, nNegVal >> 1); 

我明白了

 32767 -32768 

这是预期的吗?

我能够想到类似的东西

 65535 >> 1 = (int) 32767.5 = 32767 -65535 >> 1 = (int) -32767.5 = -32768 

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

这种理解是否正确?

看起来您的实现可能正在使用两个补码数进行算术位移。 在这个系统中,它将所有位移到右边,然后用最后一位的副本填充高位。 因此,对于您的示例,将int视为32位:

 nPosVal = 00000000000000001111111111111111 nNegVal = 11111111111111110000000000000001 

轮class后,你有:

 nPosVal = 00000000000000000111111111111111 nNegVal = 11111111111111111000000000000000 

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

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

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

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

他们声明的理由 :

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

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

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

  65535: 00000000000000001111111111111111 -65535: 11111111111111110000000000000001 

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

  65535 >> 1: 00000000000000000111111111111111 -65535 >> 1: 11111111111111111000000000000000 

转换回十进制:

  65535 >> 1 = 32767 -65535 >> 1 = -32768 

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

右移时,丢弃最低有效位。

0xFFFF = 0 1111 1111 1111 1111,右移给0 0111 1111 1111 1111 = 0x7FFF

-0xFFFF = 1 0000 0000 0000 0001(2s补码),右移至1 1000 0000 0000 0000 = -0x8000

A-1:是的。 0xffff >> 1是0x7fff或32767.我不确定-0xffff是做什么的。 这很特别。

A-2:转移与分裂不是一回事。 它是位移 – 一个原始的二进制操作。 它有时可用于某些类型的划分是方便的,但并不总是相同。

在C级之下,机器具有完全整数或标量的CPU核心。 虽然现在每个桌面CPU都有一个FPU,但情况并非总是如此,即使在今天,嵌入式系统也没有浮点指令。

今天的编程范例和CPU设计和语言可以追溯到FPU可能不存在的时代。

因此,CPU指令实现定点操作 ,通常被视为纯整数操作。 只有当程序声明floatdouble项时,才会存在任何分数。 (好吧,你可以将CPU操作用于分数的“固定点”,但现在这种情况并非常罕见。)

无论多年前语言标准委员会要求什么,所有合理的机器都会在有符号数的右移上传播符号位。 无符号值的右移在左侧以零的forms移位。 向右移出的位落在地板上。

为了进一步了解,您需要研究“二进制补码算术”。