符号扩展按位移位操作(Sign extension with bitwise shift opera

2019-08-18 09:09发布

下面这个 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声明为整数?

Answer 1:

来自: 用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

见我又写符号传播 ,所以,最左边的三个零是由于签位。

因此,这是运营商>> 签名右移做,并保留左操作数的符号



Answer 2:

为什么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的补码表示和我已经表现出的行为/解释。



Answer 3:

Becaue t是0或-1,〜t是也总是-1或0。

这是由于第(实现定义)的行为或(i - 128) >> 31 ,其基本上副本(I-128)的顶部位[假定32位整数]。 如果i是> 128,它会导致在顶部位零。 如果i是小于128,结果是否定的,所以最高位被置位。

由于~t是“相对的所有位t ”,可以期待的是t总是为0xffffffff如果t为零。



Answer 4:

>>操作,右移,在大多数编译器算术右移,这意味着除以2。

所以,如果,例如int i ==-4 (0xfffffffc),则i>>1 == -2 (0xfffffffe)。

话虽如此,我建议你检查你的代码的组装。
例如86有2个单独的指令- shrsar ,分别表示逻辑移位与算术移位。
一般情况下,编译器使用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


Answer 5:

在C和C ++的规则是负值的右移的结果定义实现。 因此,阅读你的编译器的文档。 你已经得到了不同的解释是有效的方法,但这些都不是通过语言定义的规定。



文章来源: Sign extension with bitwise shift operation