下面这个 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
声明为整数?
来自: 用C右移负数
编辑:根据科最新的6.5.7 标准草案 ,对负数这种行为是依赖实现:
E1 >> 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
注意:最左边的三位是一个因为每个班次符号位是保留的,每个位是正确的了。 我写的符号被传播 ,因为所有这三位是因为符号(而不是数据)。
[回答]
在你的输出
前八行
~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 (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
见我又写符号传播 ,所以,最左边的三个零是由于签位。
因此,这是运营商>>
签名右移做,并保留左操作数的符号 。
为什么T =(I - 128)>> 31给出零或-1每个号码?
当一个非负32位整数右移31位,所有非零位都移出,最显著位都用0填充,所以你最终0。
通常情况下,当负32位整数右移31位,最显著位没有得到填充0,而不是他们得到设置为数字的符号等标志传播到所有的位,以2的补码表示所有位设定为1倍的量为-1。 最终的效果是,如果你反复除以2的数量,但有一点扭曲......结果是朝着负无穷大,而不是朝着0。例如四舍五入-2>>1==-1
,但-3>>1==-2
和-5>>1==-3
。 这就是所谓的算术右移 。
当我说“一般”,我指的是事实,即C标准允许负值右移几个不同的行为。 最重要的是,它允许符号整数非2的补码表示。 通常情况下,然而,你有上面的2的补码表示和我已经表现出的行为/解释。
Becaue t
是0或-1,〜t是也总是-1或0。
这是由于第(实现定义)的行为或(i - 128) >> 31
,其基本上副本(I-128)的顶部位[假定32位整数]。 如果i
是> 128,它会导致在顶部位零。 如果i
是小于128,结果是否定的,所以最高位被置位。
由于~t
是“相对的所有位t
”,可以期待的是t
总是为0xffffffff如果t
为零。
该>>
操作,右移,在大多数编译器算术右移,这意味着除以2。
所以,如果,例如int i ==-4
(0xfffffffc),则i>>1 == -2
(0xfffffffe)。
话虽如此,我建议你检查你的代码的组装。
例如86有2个单独的指令- shr
& sar
,分别表示逻辑移位与算术移位。
一般情况下,编译器使用shr
无符号变量和(逻辑移位) sar
(算术移位)的符号变量。
下面是C语言代码与相应的组件,与所生成的gcc -S
:
AC:
int x=10;
unsigned int y=10;
int main(){
unsigned int z=(x>>1)+(y>>1);
return 0;
}
如:
.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
在C和C ++的规则是负值的右移的结果定义实现。 因此,阅读你的编译器的文档。 你已经得到了不同的解释是有效的方法,但这些都不是通过语言定义的规定。