首页 > 代码库 > 关于移位的有意思的小问题

关于移位的有意思的小问题

首先,直接上题目:

根据X得到F和G,其中X、F、G均是有符号的32位整型数,其中F = X/2; G = X>>1; 发现 F != G,下面的说法哪个是正确的:

A  编译错误   B  X是奇数  C X是负数  D F-G=1

上述4个选项中,首先排除A和B,显然随便举个反例就有了。

对于C,这里就涉及到负数在计算机中的表示形式了,至于怎么个表示法,下面一一道来:

对于负数,在计算机内的表示方法是怎么样的呢?

下面的代码可以说明一切:

void print(int x)
{
    //这里将形参的int转化为unsigned int,是为了说明负数的补码表示方法而已。
    unsigned int tmp = x;
    cout << tmp << endl;
    string s;
    while(tmp)
    {
        if(tmp & 1)  s.push_back('1');
        else s.push_back('0');
        tmp >>=1;
    }
    reverse(s.begin(),s.end());
    cout << s << endl;
}
上述代码的执行结果, x = 9 时,执行结果为: 
1111 1111 1111 1111 1111 1111 1111 0111

这里的补码的表示其实很简单,转化过程无非是原码,反码和补码的顺序而已:

原码: 1 000 0000 0000 0000 0000 0000 0000 1001 //最高位是符号位,后面的31位是数字位

反码: 1 111 1111 1111 1111 1111 1111 1111 0110 //除了符号位,其他为全部变反

补码: 1 111 1111 1111 1111 1111 1111 1111 0111 //反码加1即可

这样的话,对于上述的代码的结果解析就这样了,现在执行 :

 F = X/2 = -9/2 = -4 ; // 这就是除法运算

 G = X>>1 = (-9) >> 1 就是将上述的补码部分向右移一位,执行过称为:

  1   111 1111 1111 1111 1111 1111 1111 0111 

  1 1 111 1111 1111 1111 1111 1111 1111 0111  //去掉最右边的蓝色的1,加上最左边的绿色的1

于是乎,上述结果变为:

  1 1 111 1111 1111 1111 1111 1111 1111 011  //这里是结果的补码,现在讲结果的补码转换为源码,从而得到原来的数值。其过程为:

  1 111 1111 1111 1111 1111 1111 1111 1011  //补码

  111 1111 1111 1111 1111 1111 1111 1010 //补码减去1 变为反码

  1000 0000 0000 0000 0000 0000 0000 0101  //除了最高位的符号位,其他为变反,

结果竟然是-5!!!! 也就是说 (-9 >> 1) = -5 !!!!

也就是说出现了上述的 F != G, F-G = 1 。于是答案是D.

关于移位的有意思的小问题