移位运算 and 原码、反码、补码

背景

在看《Absolute C++》时遇到的。判断以下代码的输出内容:

int i=1;
while (i<<=10)
{
    cout << i << endl;
    i += 3;
}

上面while判别式中i<<=10可不是小于或远小于的大小关系比较,而是移位运算的表达式,相当于i = i << 10。那么结果如何呢?

  • C++中赋值是一种表达式,该表达式的返回值为所赋的值
  • 那也就是看上面的代码中,“先左移10位,再加十进制3”这一对操作,会不会有某一次使左移之后的值为0

什么是移位运算,如何做

各种变量在计算机中都是以二进制的形式存在,比如十进制1就是10,移位运算就是直接操作该变量的二进制数,二进制又有“原码、反码、补码”(看这篇笔记二进制:原码反码与补码),移位运算的操作对象是补码

移位的分类,根据移位方向不同,分为:

  • 左移位<<:符号位不参与移动,把二进制补码整体向左移动多少位
    • 无论正数还是负数,向左移动之后右边空出来的位,用0补
  • 右移位>>:符号位不参与移动,二进制补码向右移动多少位
    • 向右移动之后左边与符号位之间空出来的位,用符号位补
      • 正数的话,符号位为0,用0补
      • 负数的话,符号位为1,用1补

int型变量在64位机器中占多少个二进制位

其实这一块主要是答疑:十进制1的二进制10,那左移2位之后为啥不是二进制00、十进制0,而是变成了十进制4.

image-20210330094809428.png

其实答案就是,十进制1的二进制10,并不是只占两个二进制位,在10两位的左侧,还有很多个二进制位。多少个呢:

int型变量一般长度为4个字节,每个字节为8个二进制位,那么总长也就是4*8=32位。

所以只要左移不超过32-2=30位,就不会移出被丢弃。

移位前后数值的大小关系

从二进制到十进制的转换可以了解其关系:

二进制:0 0 1 1 0 1 0
十进制:$ 2^4 + 2^3 + 2^1$

如果左移1位:

二进制:0 1 1 0 1 0 0
十进制:$ 2^5+2^4+2^2=2*(2^4 + 2^3 + 2^1)$

结论:

  • 左移的时候,如果没有移出左边界,则 结 果 = 原 数 ∗ 2 移 动 的 位 数 结果 = 原数 * 2^{移动的位数} =2

    • 怎么判断有没有移出呢?该数原来的二进制数最左侧的1在第几位 and 该类型的所占位数
  • 右移的时候,如果没有移出左边界,则 结 果 = 原 数 / 2 移 动 的 位 数 结果 = 原数 / 2^{移动的位数} =/2

回到文章开头的题目

int i=1;
while (i<<=10)
{
    cout << i << endl;
    i += 3;
}

十进制1与十进制3的原码与补码:

  • 根据原码可以直接获得补码,而不需要经过反码转换。补码与原码的关系为:
    • 在原码的基础上,右数第一个1及右边的0保持不变,左边的各位取反,符号位保持不变
            原  码                       补  码
1   0000 0000 ... 0000 0001     0111 1111 ... 1111 1111
3   0000 0000 ... 0000 0011     0111 1111 ... 1111 1101
    -----------------------     -----------------------
            共32位                       共32位
0   0000 0000 ... 0000 0000     0000 0000 ... 0000 0000

注意int型,32位,可表示范围为[2^32/2, 2^32/2=214 748 364 8)

首先,对补码进行左移位操作,每次左移10位,移位之后右边的空位补0;

其次,while判别式中实际上是一个表达式 i = i << 10

那也就是判断,每次左移10位再加十进制3,会不会有一次遇到i=0的情况:

  • 第一次判断时, i = 1 ∗ 2 10 = 1024 i=1*2^{10}=1024 i=1210=1024,非0,进入while
    • +3, i = 2 10 + 3 = 1027 i=2^{10}+3=1027 i=210+3=1027
  • 第二次判断时, i = 1027 ∗ 2 10 = 1051648 i=1027*2^{10}=1051648 i=1027210=1051648,非0,进入while
    • +3, i = 1051648 + 3 = 1051651 i=1051648+3=1051651 i=1051648+3=1051651
  • 第三次判断时, i = 1051651 ∗ 2 10 = 1076890624 i=1051651*2^{10}=1076890624 i=1051651210=1076890624,非0, 且没有超出右边界,进入while
    • +3, i = 1076890624 + 3 = 1027890627 i=1076890624+3=1027890627 i=1076890624+3=1027890627
  • 第4次及之后判断时,$i_{n-1}*1024 > 右边界,输出结果不确定性

这样二进制与十进制转换,太麻烦了,如果只是判断while循环会不会停止的话,可以仅分析二进制。0的二进制各位均为0,只要看某次+3(二进制再左移10位后是否各位均为0即可:

  • 十进制1的二进制,仅最右侧有1个1,左移10位后,右边10位均为0;
  • 加十进制3后,看上面3的补码,右边10位有9个1,所相加之后右10位与3的补码相同,含有1
  • 进入while判别式,再左移10位,然后再加3,右边的9个1一直无法移出去,也就是说始终无法达到全0,即十进制0,所以while无限循环。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值