提示与建议:同学们在自学位运算符相关知识后配合本文巩固已学内容,效果更佳哦
今天我们来学习一下只用位运算符来模拟加减乘除法。
【1.加法】
不管是十进制加法还是二进制加法,其加的过程在每一位看,分为和、进位两个部分。和要留在当前位,进位加入到下一位。
我们现在关注二进制加法。发现一个特点。
位的异或运算跟求和的结果一致:
异或 1^1=0 1^0=10^0=0
求和 1+1=0 1+0=10+0=0
位的与运算跟求进位的结果一致:
位与 1&1=11&0=0 0&0=0
进位 1+1=1 1+0=00+0=0
于是我们决定用异或运算和与运算来表示加法。
【2.减法】
减法其实是用加法来实现的。求a-b,其实是求[a-b]补。因为有[a-b]补=[a]补 - [b]补= [a]补+[-b]补。所以我就要先求出-b。求一个数的负,操作是将其连符号位一起取反然后加1,相信同学们在计算机导论课上掌握了原理,不再赘述。
于是有办法做减法了:先把减数求负,然后做加法
【3.乘法】
乘法操作时即使用补码也要需要考虑符号位了,所以要先把符号拿出来单独计算。
因为本文只使用位运算知识,我们引入两个工具函数。
接下来,我们仔细思考,位运算如何相乘?
一种方法是用循环加法,a*b表示为b个a相加,时间复杂度为O(N);另一种方法是在2进制位上做乘法,时间复杂度为O(log2N)。
我们选用第二种方法来做乘法。
【4.除法】
做除法的第一种思路,从被除数上减去除数,看能减多少次之后变得不够减。时间复杂度为O(N);第二种思路,从除数*最大倍数开始测试,如果比被除数小,则要减去。下一轮让除数的倍数减少为上一次倍数的一半,这样的直到倍数为1时,就能把被除数中所有的除数减去,并得到商。时间复杂度降低到O(log2N)。例如计算137/7时,137<7*231,137<7*230…137>7*24,
于是137-7*16=25。25>7*2,25-7*2=11。11>7*1,11-7=4。4<7,结束。此时算出商为16+2+1=19。代码如下
【小结】
1、注意考虑除法运算的特殊情况:除数不为0。
2、int整型的表示范围是-2147483648~+2147483647。所以在求负的时候,有一个数-2147483648是不可以求负的,因为int无法表示+2147483648这个数。对于乘法运算,如果出现了这个数,运算就会溢出。对于除法操作,如果被除数是-2147483648,那么不能直接求负。所以最好采用无符号数来进行运算。
3、231,230,......,64,32,16,8,4,2,1。为什么尝试用这样的倍数做减法就能把所有除数都减去呢?从二进制数的角度来看,这些权值就可以构成所有的整数,组成倍数不会有遗漏。从折半的思想来看,这样是一个逐步折半细化的过程。
4、可能会有同学吐槽,用位运算模拟加减乘除不是吃力不讨好吗?何必呢?确实,这篇文章是帮同学们巩固位运算的知识的,也是开拓同学们的思维的。但位运算在有些方面确实是有优势的。比如一般我们会用if(i%2==0)判断偶数,但写成if(!(i&1))来判断,在判断很多奇偶数的情况下,时间复杂度会有常数级别的减小。更小的时间复杂度正是程序员所追求的。此外,还有很多计算用位运算都能得到加快。这些就等同学们自己探索吧!
Ps:假如同学们耐心看到这里,我想吐槽一下,还有更多高手有更透彻的讲解,同学们一定要多多自学哦。