0x9*_*x90 5 c c++ bit-manipulation
在此问答后,我试着检查答案,所以我写道:
#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;
}
Run Code Online (Sandbox Code Playgroud)
输出是:
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
Run Code Online (Sandbox Code Playgroud)
如果声明为整数,为什么~t任何负数?-1 == 0xFFFFFFFFt
为什么 t = (i - 128) >> 31 为每个数字给出零或 -1?
当一个非负的 32 位整数右移 31 个位置时,所有非零位都被移出并且最高有效位被填充为 0,因此最终为 0。
通常,当负 32 位整数右移 31 个位置时,最高有效位不会填充为 0,而是将它们设置为数字的符号,因此符号会传播到所有位并以 2 的补码表示所有设置为 1 的位等于 -1。最终效果就好像你反复将数字除以 2,但有一点扭曲......结果向 -infinity 舍入而不是向 0 舍入。例如-2>>1==-1but-3>>1==-2和-5>>1==-3。这称为算术右移。
当我说“通常”时,我的意思是 C 标准允许负值右移的几种不同行为。最重要的是,它允许有符号整数的非 2 的补码表示。然而,通常你有 2 的补码表示和我在上面展示/解释的行为。
来自:C 中的负数右移
编辑: 根据最新草案标准的第 6.5.7 节,负数的这种行为取决于实现:
E1 >> E2 的结果是 E1 右移 E2 位位置。如果 E1 具有无符号类型或者如果 E1 具有有符号类型和非负值,则结果的值是 E1 / 2 E2的商的整数部分。如果 E1 具有有符号类型和负值,则结果值是实现定义的。
而且,您的实现可能正在使用二进制补码进行算术移位
运算符>>为 Signed right shift或算术右移,将所有位右移指定的次数。重要的是>> 在移位后将最左边的符号位(Most Significant Bit MSB)填充到最左边的位。这称为符号扩展,用于在右移负数时保留负数的符号。
下面是我的图解表示和一个例子来展示它是如何工作的(对于一个字节):
示例:
i = -5 >> 3; shift bits right three time
Run Code Online (Sandbox Code Playgroud)
五进二出的补码形式是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
Run Code Online (Sandbox Code Playgroud)
下面是,如何>>工作?当你做-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
Run Code Online (Sandbox Code Playgroud)
注意:最左边的三位是 1,因为在每个移位符号位都被保留并且每一位也是正确的。我写了符号被传播,因为所有这三个位都是因为符号(而不是数据)。
[ANSWER]
在你的输出中
前八行
~t is 0
==> t is FFFFFFFF
==> t is -1
Run Code Online (Sandbox Code Playgroud)
(注意:-1 的 2 的补码是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;
Run Code Online (Sandbox Code Playgroud)
的值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
Run Code Online (Sandbox Code Playgroud)
因为在您的系统中 int 是 4 字节 = 32 位,所以大多数31位都被移出和丢失,并且由于符号位的传播,1对于负数,所有位值都变为1。(正如我在上图中显示的一个字节)
所以拳头八次:
t = -ve number >> 31 == -1
t = -1
and this gives
~t = 0
Run Code Online (Sandbox Code Playgroud)
因此,~t 的拳头八次的输出为 0。
对于剩余的最后几行
~t is FFFFFFFF
==> ~t is -1
==> t is 0
Run Code Online (Sandbox Code Playgroud)
对于剩余的最后几行,在 for 循环中
for (i=120;i<140;i++){
t = (i - 128) >> 31;
Run Code Online (Sandbox Code Playgroud)
i 值均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
Run Code Online (Sandbox Code Playgroud)
再看我写的符号是传播的,所以最左边的三个零是由于符号位。
所以这就是运算符有>> 符号右移所做的,并保留左操作数的符号。