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 -*-

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于C++&OPENCV 的全景图像拼接 C++是一种广泛使用的编程语言,它是由Bjarne Stroustrup于1979年在新泽西州美利山贝尔实验室开始设计开发的。C++是C语言的扩展,旨在提供更强大的编程能力,包括面向对象编程和泛型编程的支持。C++支持数据封装、继承和多态等面向对象编程的特性和泛型编程的模板,以及丰富的标准库,提供了大量的数据结构和算法,极大地提高了开发效率。12 C++是一种静态类型的、编译式的、通用的、大小写敏感的编程语言,它综合了高级语言和低级语言的特点。C++的语法与C语言非常相似,但增加了许多面向对象编程的特性,如类、对象、封装、继承和多态等。这使得C++既保持了C语言的低级特性,如直接访问硬件的能力,又提供了高级语言的特性,如数据封装和代码重用。13 C++的应用领域非常广泛,包括但不限于教育、系统开发、游戏开发、嵌入式系统、工业和商业应用、科研和高性能计算等领域。在教育领域,C++因其结构化和面向对象的特性,常被选为计算机科学和工程专业的入门编程语言。在系统开发领域,C++因其高效性和灵活性,经常被作为开发语言。游戏开发领域中,C++由于其高效性和广泛应用,在开发高性能游戏和游戏引擎中扮演着重要角色。在嵌入式系统领域,C++的高效和灵活性使其成为理想选择。此外,C++还广泛应用于桌面应用、Web浏览器、操作系统、编译器、媒体应用程序、数据库引擎、医疗工程和机器人等领域。16 学习C++的关键是理解其核心概念和编程风格,而不是过于深入技术细节。C++支持多种编程风格,每种风格都能有效地保证运行时间效率和空间效率。因此,无论是初学者还是经验丰富的程序员,都可以通过C++来设计和实现新系统或维护旧系统。3

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值