C中的右移负数

C中的右移负数,c,bit-shift,negative-number,C,Bit Shift,Negative Number,我有C代码,我在其中执行以下操作 int nPosVal = +0xFFFF; // + Added for ease of understanding int nNegVal = -0xFFFF; // - Added for valid reason 现在当我尝试 printf ("%d %d", nPosVal >> 1, nNegVal >> 1); 我明白了 这是预期的吗 我能像这样思考 65535 >> 1 = (int) 32767.

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

int nPosVal = +0xFFFF;   // + Added for ease of understanding
int nNegVal = -0xFFFF;   // - Added for valid reason
现在当我尝试

printf ("%d %d", nPosVal >> 1, nNegVal >> 1);
我明白了

这是预期的吗

我能像这样思考

65535 >> 1 = (int) 32767.5 = 32767
-65535 >> 1 = (int) -32767.5 = -32768
也就是说,-32767.5四舍五入为-32768


这种理解正确吗?

右移时,最低有效位将被丢弃

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


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

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

A-1:是。0xffff>>1是0x7fff或32767。我不确定-0xffff做什么。真奇怪


A-2:移动和分割不是一回事。它是位移位——一种基本的二进制操作。它有时可以用于某些类型的除法是很方便的,但并不总是一样的。

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

      65535: 00000000000000001111111111111111
     -65535: 11111111111111110000000000000001
位向右移动一位,向左扩展(请注意,这取决于实现,感谢Trent):

转换回十进制:

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

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

nPosVal = 00000000000000001111111111111111
nNegVal = 11111111111111110000000000000001
轮班后,您有:

nPosVal = 00000000000000000111111111111111
nNegVal = 11111111111111111000000000000000
如果将其转换回十进制,则分别得到32767和-32768

实际上,右移会朝着负无穷大的方向旋转

编辑:根据最新版本的第6.5.7节,负数上的这种行为取决于实现:E1>>E2的结果是E1右移E2位位置。如果E1具有无符号类型或E1具有有符号类型和非负值,则结果值为E1/2E2商的整数部分。如果E1具有有符号类型和负值,则结果值由实现定义

他们对此表示:C89委员会确认了K&R授予的执行自由,即不要求签名右移操作进行签名扩展,因为这样的要求可能会降低快速代码的速度,并且签名扩展移位的用处微乎其微。(转移负二的补码) 算术上正确的整数一位不等于除以二!)


因此,它在理论上依赖于实现。实际上,我从未见过一个实现在左操作数有符号时不进行算术右移。

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

今天的编程模式、CPU设计和语言可以追溯到FPU甚至不存在的时代

因此,CPU指令实现定点操作,通常被视为纯整数运算。只有当程序声明float或double项时,才会存在任何分数。(好吧,你可以用CPU操作来表示分数的“定点”,但这在现在和过去都是非常罕见的。)

不管几年前语言标准委员会要求什么,所有合理的机器都会在有符号数字的右移上传播符号位。无符号值的右移位在左侧以零移位。右边移位的钻头掉在地板上


为了进一步理解,您需要研究“两个补运算”。

注意,左侧的扩展依赖于实现。标准规定:“如果右侧操作数的值为负数或大于或等于提升后的左侧操作数的宽度,则行为未定义。”@特伦特:你确定吗?我认为符号扩展取决于左操作数的符号性。@Gonzalo:标准确实这么说,但在本例中,右操作数是
1
。这不是负数,也不是
int
的宽度。我认为你对“合理机器”的定义很窄。如果我的定义很窄,那么请指定一台机器,它的
x>>1
,在C中,会将负数变成正数。微芯片C18编译器(可以在这里找到用户指南的链接:-参见第B.4节)呵呵,好的。我能说的就是casibus non exceptis中的exception probat Reglam,也称为“证明规则的异常”,参见:+1;-),感谢您对FP运算和整数运算的解释。事实上,我想知道右移2的补码的奇怪行为。这与除法不同。整数文本是有符号整数,因此
-0xFFFF
0xFFFF
取反。也就是说,它等于
(~0xFFFF)-1
,被解释为有符号整数。Q-1询问结果是否符合预期。我的回答是:不,你不能在没有先查阅你的编译器文档的情况下,期望得到负数右移的任何给定结果。你能给我一个链接吗,标准上是这么说的?+1,这就是我想知道的。右移向负无穷大。但是它有文档记录吗?它取决于实现。(见我上面的编辑。)正如我所说,我从未见过一个实现在这方面有所不同,但理论上是可以的。右位移位功能为“地板除法”。它总是朝着负无穷远的方向旋转。在Python语法中:
-5//4=-2
nPosVal = 00000000000000001111111111111111
nNegVal = 11111111111111110000000000000001
nPosVal = 00000000000000000111111111111111
nNegVal = 11111111111111111000000000000000