32位进位选择加法器_剑指面试题[65]——不用加减乘除做加法(位运算)

题目描述:不用加减乘除做加法_牛客网

写一个函数,求两个整数之和,要求在函数体内不得使用+、-、*、/四则运算符号。

解法(1):空间O(1),时间O(n)。

不能用四则运算,只能用二进制位运算模拟。二进制中,异或是不进位的加法,进位可以用相与并左移1得到,继续令二者相加直到进位为0。

举例,4+6=10。

①用‘与运算+右移1’模拟进位位置,用异或模拟不进位的相加结果。

②得到8和2。重复①,直至没有进位。

v2-c24958fe64c949a7634eb0bb228af836_b.jpg
# 死循环版

死循环原因:当进位结果导致原有储存位数扩增时,异或结果为负数时位数扩增体现为数值位左边补1,则两者的进位将永远不为0,陷入死循环。陷入死循环的场景是 负数+正数,且正数的绝对值比负数大。

分析:因为正数的绝对值大,所以用多少位存储取决于正数,设数值位用n-1位存储,第n位是符号位,正数的数值位最高位即第n-1位是1。由于负数绝对值较小,因此补码的数值位值大于正数数值位,因此负数的数值位最高位即第n-1位也是1,两个加数相与后得到最高数值位即第n-1位是1的正数。两个加数异或后得到一个n位负数。相与结果左移一位得到进位结果,进位结果的数值位是n位,因此异或结果的负数也必须用n位数值位表示,则负数由n+1位表示,第n位和第n+1位符号位均为1。新的加数为该负数和左移结果。继续重复上面的操作,由于进位结果和负数的数值最高位都为1,相与后又会导致数位扩增,进位结果永远不为0,陷入死循环。举例如下图。

示例分析:-1+2得到的不进位和、进位数(两个加数)的二进制形式为11{0...}1、01{0...}0,其中{}内表示循环过程中增加的1或0的个数。可以看出,当进位只有最高数值位为1时,继续操作将不断左移最高位1,不进位和将不断增加数值位中的0,而正确和可以表示为{0...}1,因此只要取出{0...}1值,即去掉符号位、数值位最高位剩下的二进制值,亦即异或结果的低n-2位返回即可。

v2-d3bf67ff45435579b1364dcb0ec6a868_b.png
不限制位数时,-1 +2陷入死循环

实现1:限制最高存储位数为32(因为测试用例不超过32位)。当进位1不断左移到达2^31时,继续左移将溢出,此时丢弃进位,返回异或结果的低30位数。

# -*- coding:utf-8 -*-

实现2:其实,当进位数的二进制位中只有一个1且1在最高的数值位上时,最终和的所有信息已经存储在不进位加和数当中,此时就可以丢弃进位了。该方法适用于事先不知道存储位数限制的情景。

总结:当两个加数一正一负,且正数的绝对值≥负数的绝对值,且正数的二进制位中只有一个1时,属于可能陷入死循环的特殊情况,提前结束,返回负数的n-2位二进制数。

# -*- coding:utf-8 -*-

其他解法

  1. 添加越界检查才能避免负数死循环。跟0xFFFFFFFF(边界数)相与,相当于将负数的二进制补码视为某个正数的原码,有(2^32)-原负数=该正数,即二者对于2^32同模。符号参与的进位1将一直往左移直到超过边界数时退出循环。最后要再将该“正数”转化为对应补码的负数。关于补码https://www.cnblogs.com/zhangziqiu/archive/2011/03/30/ComputerCode.html
  2. 正负数判断及还原:对于负数来说,~((n&0xFFFFFFFF)^0xFFFFFFFF)=n。

以-15为例,~(-15)=241^0xFF=((-15)&0xff)^0xff。

v2-be0ce3a9b4d7e5ec52bef58df7e88ed4_b.png
241(与-15对256同模,即对(2**8)同模) 对应的二进制数为: 11110001

11110001^0xFF -----> 00001110(14),相当于00001111-1,即15-1。

~(00001110) -----> 11110001(补码) ,对应的十进制数是 -15

v2-5f48666d91fde9e3e1bd9770a99964cd_b.png

此处有一个规律:~n = -(n+1)。取反改变符号!!

总结:对于负数a,

  • 令b=a&0xFFFFFFFF,相当于将a往前拨2**32步得到正数b,即b=a+2**32;
  • 令c=b^0xFFFFFFFF,相当于求(2**8-1)减去b得到正数c,即c=(2**8-1)-b;
  • 由上面二式可得,a=-c-1,即c比a的绝对值小1;
  • 令d=~c,相当于将c往后拨2**8-1步得到负数d,由公式得d=~c=-(c+1)=a。

*设置成32位应该是考虑到其他语言的特点,测试样例中不会出现超过32位整型的数,实际上,把边界调大的话,不会影响最终结果。参考https://blog.csdn.net/lrs1353281004/article/details/87192205

# -*- coding:utf-8 -*-

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值