关注this Q& A我试着检查答案,所以我写道:
#include <stdio.h>
int main ()
{
int t;int i;
for (i=120;i<140;i++){
t = (i - 128) >> 31;
printf ("t = %X , i-128 = %X , ~t & i = %X , ~t = %X \n", t, i-128 , (~t &i), ~t);
}
return 0;
}
,输出为:
t = FFFFFFFF , i-128 = FFFFFFF8 , ~t & i = 0 , ~t = 0
t = FFFFFFFF , i-128 = FFFFFFF9 , ~t & i = 0 , ~t = 0
t = FFFFFFFF , i-128 = FFFFFFFA , ~t & i = 0 , ~t = 0
t = FFFFFFFF , i-128 = FFFFFFFB , ~t & i = 0 , ~t = 0
t = FFFFFFFF , i-128 = FFFFFFFC , ~t & i = 0 , ~t = 0
t = FFFFFFFF , i-128 = FFFFFFFD , ~t & i = 0 , ~t = 0
t = FFFFFFFF , i-128 = FFFFFFFE , ~t & i = 0 , ~t = 0
t = FFFFFFFF , i-128 = FFFFFFFF , ~t & i = 0 , ~t = 0
t = 0 , i-128 = 0 , ~t & i = 80 , ~t = FFFFFFFF
t = 0 , i-128 = 1 , ~t & i = 81 , ~t = FFFFFFFF
t = 0 , i-128 = 2 , ~t & i = 82 , ~t = FFFFFFFF
t = 0 , i-128 = 3 , ~t & i = 83 , ~t = FFFFFFFF
t = 0 , i-128 = 4 , ~t & i = 84 , ~t = FFFFFFFF
t = 0 , i-128 = 5 , ~t & i = 85 , ~t = FFFFFFFF
t = 0 , i-128 = 6 , ~t & i = 86 , ~t = FFFFFFFF
t = 0 , i-128 = 7 , ~t & i = 87 , ~t = FFFFFFFF
t = 0 , i-128 = 8 , ~t & i = 88 , ~t = FFFFFFFF
t = 0 , i-128 = 9 , ~t & i = 89 , ~t = FFFFFFFF
t = 0 , i-128 = A , ~t & i = 8A , ~t = FFFFFFFF
t = 0 , i-128 = B , ~t & i = 8B , ~t = FFFFFFFF
如果~t
声明为整数,为什么-1 == 0xFFFFFFFF
的任何负数为t
?
答案 0 :(得分:4)
为什么t =(i-128)&gt;&gt; 31给每个数字零或-1?
当非负32位整数向右移位31个位置时,所有非零位都会移出,最高位将被0填充,因此最终得到0。
通常,当负32位整数向右移位31个位置时,最高有效位不会被0填充,而是将它们设置为数字的符号,因此符号传播到所有位并且2的补码表示所有位设置为1到-1。净效果就像你重复将数字除以2,但稍微扭曲......结果向-infinity舍入而不是向0.舍入为例如-2>>1==-1
但是-3>>1==-2
和{{ 1}}。这称为算术右移。
当我说“通常”时,我的意思是C标准允许几种不同的行为用于负值的右移。最重要的是,它允许有符号整数的非2的补码表示。但是,通常情况下,你有2的补码表示和我在上面展示/解释过的行为。
答案 1 :(得分:3)
来自:Right shifting negative numbers in C
编辑:根据最新draft standard的第6.5.7节,负数上的这种行为取决于实现:
E1的结果&gt; &GT; E2是E1右移E2位位置。如果E1具有无符号类型或者E1具有有符号类型和非负值,则结果的值是E1 / 2 E2 的商的整数部分。如果E1具有有符号类型和负值,则结果值是实现定义的。
并且,您的实现可能正在使用两个补码
进行算术移位运算符>>
为签名右移或算术右移,将所有位向右移动指定的次数。重要的是>>
将最左边的符号位(最高有效位MSB)填充到移位后最左边的位。这称为符号扩展,用于在向右移动时保留负数的符号。
下面是我的图解表示,其中有一个示例来说明这是如何工作的(对于一个字节):
示例:
i = -5 >> 3; shift bits right three time
五分之二的补码形式是1111 1011
记忆表示:
MSB
+----+----+----+---+---+---+---+---+
| 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 |
+----+----+----+---+---+---+---+---+
7 6 5 4 3 2 1 0
^ This seventh, the left most bit is SIGN bit
以下是>>
如何运作?当你-5 >> 3
this 3 bits are shifted
out and loss
MSB (___________)
+----+----+----+---+---+---+---+---+
| 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 |
+----+----+----+---+---+---+---+---+
| \ \
| ------------| ----------|
| | |
▼ ▼ ▼
+----+----+----+---+---+---+---+---+
| 1 | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
+----+----+----+---+---+---+---+---+
(______________)
The sign is
propagated
注意:最左边的三个位是1,因为每个移位符号位都被保留,每个位也是正确的。我写了符号传播因为所有这三个位都是因为符号(而不是数据)。
<强> [ANSWER] 强>
在
前八行
~t is 0
==> t is FFFFFFFF
==> t is -1
(注意: 2的-1的补码是FFFFFFFF
,因为1 = 00000001
,1的1的补码是FFFFFFFE
,2的补码= 1的补码+1是: FFFFFFFE
+ 00000001
= FFFFFFFF
)
所以t
始终在循环中的前八次评估-1
。 是,怎么样?
for for循环
for (i=120;i<140;i++){
t = (i - 128) >> 31;
前八次i
的值为i = 120, 121, 122, 123, 124, 125, 126 ,127
,所有八个值小于128 。所以返回(i - 128) = -8, -7, -6, -5, -4, -3, -2, -1
。因此,在前八次表达式t = (i - 128) >> 31
中,移位权利为负数。
t = (i - 128) >> 31
t = -ve number >> 31
因为在你的系统中int是4字节= 32位,所以最右边的31
位是移出和丢失,而是由于符号位传播1
为负数字所有位值变为1
。 (正如我在上图中显示的一个字节)
所以对于拳头八次:
t = -ve number >> 31 == -1
t = -1
and this gives
~t = 0
因此~t的第八次输出为0.
剩余的最后一行
~t is FFFFFFFF
==> ~t is -1
==> t is 0
对于剩余的最后一行,在for循环中
for (i=120;i<140;i++){
t = (i - 128) >> 31;
我的值128, 129, 130, 132, 133, 134, 135, 136, 137, 138, 139,
都大于或等于 128.且符号位为0
。
因此(i - 128)剩余的最后一行是>=0
,所有这个MSB符号位= 0
。并且因为再次向右移动31次除了所有比特,然后叹息位移出并且符号位0
传播并用0
填充所有位,并且幅度变为0
。
我认为如果我也为一个正数写一个例子会很好。我们举一个例子5 >> 3
,五个是一个字节是0000 0101
this 3 bits are shifted
out and loss
MSB (___________)
+----+----+----+---+---+---+---+---+
| 0 | 0 | 0 | 0 | 0 | 1 | 0 | 1 |
+----+----+----+---+---+---+---+---+
| \ \
| ------------| ----------|
| | |
▼ ▼ ▼
+----+----+----+---+---+---+---+---+
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
+----+----+----+---+---+---+---+---+
(______________)
The sign is
propagated
再次看到我写标志传播,所以最左边的三个零是由于符号位。
这就是操作符>>
签名右移的操作,保留左操作数的符号。
答案 2 :(得分:2)
Becaue t
为0或-1,〜t也始终为-1或0.
这是由于(实现定义的)行为或(i - 128) >> 31
,它基本上复制了(i-128)的最高位[假设32位整数]。如果i
是&gt; 128,它将导致顶部位为零。如果i
小于128,则结果为负,因此设置了最高位。
由于~t
是“t
的所有位”,如果t
为零,您可以预期t
始终为0xffffffff。
答案 3 :(得分:1)
>>
运算符右移是大多数编译器中的算术右移,意味着除以2。
所以,如果,例如, int i ==-4
(0xfffffffc),然后i>>1 == -2
(0xfffffffe)。
话虽如此,我建议您检查代码的汇编
例如x86有2个单独的指令 - shr
&amp; sar
,表示逻辑转变&amp;算术移位
通常,编译器使用shr
(逻辑移位)来表示无符号变量&amp;签名变量的sar
(算术移位)。
以下是C代码&amp;相应的程序集,使用gcc -S
:
交流转换器:
int x=10;
unsigned int y=10;
int main(){
unsigned int z=(x>>1)+(y>>1);
return 0;
}
A.S:
.file "a.c"
.globl x
.data
.align 4
.type x, @object
.size x, 4
x:
.long 10
.globl y
.align 4
.type y, @object
.size y, 4
y:
.long 10
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl x, %eax
sarl %eax ; <~~~~~~~~~~~~~~~~ Arithmetic shift, for signed int
movl y, %edx
shrl %edx ; <~~~~~~~~~~~~~~~~ Logical shift, for unsigned int
addl %edx, %eax
movl %eax, -4(%ebp)
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
.section .note.GNU-stack,"",@progbits
答案 4 :(得分:0)
C和C ++中的规则是负值右移的结果是实现定义的。请阅读编译器的文档。您获得的各种解释都是有效的方法,但这些都不是语言定义所强制要求的。