背景
在看《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.
其实答案就是,十进制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=1∗210=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=1027∗210=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=1051651∗210=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无限循环。