【剑指offer48】不用加减乘除做加法(涉及到位运算)

位运算

二进制运算

逢二进一

a = 35;
b = 47;
c = a + b;

在这里插入图片描述

原码和补码

1的原码:
在这里插入图片描述
-1的原码:
在这里插入图片描述
正数的补码等于原码。
负数的补码等于其原码按位取反后(除了最高位)加1,比如-1的补码就是32个1。
在这里插入图片描述
使用补码的好处在于,可以将符号位和数值位统一处理,加法与减法也可以统一处理。 比如3 - 1,等价于3 + (-1),则对于计算机来说将3和-1的补码直接相加就可以
在这里插入图片描述

位运算概述

在这里插入图片描述

按位与运算符(&)

定义:参加运算的两个数据,按二进制位进行“与”运算。
运算规则
0&0=0 0&1=0 1&0=0 1&1=1
总结:两位同时为1,结果才为1,否则结果为0。
例如:3&5 即 0000 0011& 0000 0101 = 0000 0001,因此 3&5 的值得1。

按位或运算符(|)

定义:参加运算的两个对象,按二进制位进行“或”运算。
运算规则
0|0=0 0|1=1 1|0=1 1|1=1
总结:参加运算的两个对象只要有一个为1,其值为1。
例如:3|5即 0000 0011| 0000 0101 = 0000 0111,因此,3|5的值得7。

异或运算符(^)

定义:参加运算的两个数据,按二进制位进行“异或”运算。
运算规则
0^0=0 0^1=1 1^0=1 1^1=0
总结:参加运算的两个对象,如果两个相应位相同为0,相异为1。

取反运算符 (~)

定义:参加运算的一个数据,按二进制进行“取反”运算。
运算规则: 
~1=0
~0=1
总结:对一个二进制数按位取反,即将0变1,1变0。

左移运算符(<<)

定义:将一个运算对象的各二进制位全部左移若干位(左边的二进制位丢弃,右边补0)。
设 a=1010 1110,a = a<< 2 将a的二进制位左移2位、右补0,即得a=1011 1000。
若左移时舍弃的高位不包含1,则每左移一位,相当于该数乘以2。

右移运算符(>>)

定义:将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃。
例如:a=a>>2 将a的二进制位右移2位,左补0 或者 左补1得看被移数是正还是负。
操作数每右移一位,相当于该数除以2。

题目描述

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

思路

计算二进制值相加
例如:5 + 7 = 12
5的二进制为101,7的二进制为111
1、101^111,异或得到个位值,010(2)。
2、计算进位值。101&111,与操作得到101,(101&111)<<1左移一位得到1010(10)。
3、1010^010,得到1000(8)。
4、1010&010,与操作得到0010,左移一位得到100(4)。
5、1000^100,得到1100(12)。
6、1000&100,得到0000。结束

python代码

# -*- coding:utf-8 -*-
class Solution:
    def Add(self, num1, num2):
        # write code here
		# 由于题目要求不能使用四则运算,那么就需要考虑使用位运算
        # 两个数相加可以看成两个数的每个位先相加,但不进位,然后在加上进位的数值
        # 如12+8可以看成1+0=1 2+8=0,由于2+8有进位,所以结果就是10+10=20
        # 二进制中可以表示为1000+1100 先每个位置相加不进位,
        # 则0+0=0 0+0=0 1+0=1 1+1=0这个就是按位异或运算
        # 对于1+1出现进位,我们可以使用按位与运算然后在将结果左移一位
        # 最后将上面两步的结果相加,相加的时候依然要考虑进位的情况,直到不产生进位
        # 注意python没有无符号右移操作,所以需要越界检查
        # 按位与运算:相同位的两个数字都为1,则为1;若有一个不为1,则为0。
        # 按位异或运算:相同位不同则为1,相同则为0。
        while num2:
        	# 0xffffffff==4294967295==2**32-1
        	result = (num1 ^ num2) & 0xffffffff  # 得到负数的补码表示。因为在计算机中,负数都是按补码进行运算
            carry = ((num1 & num2) << 1) & 0xffffffff # 得到负数的补码表示
            num1 = result
            num2 = carry
        # 正数:原码等于补码
        if num1 <= 0x7fffffff:
        	# 0x7fffffff==2147483647==2**31-1
            result = num1
        # 负数:补码等于原码取反加1
        else:
        	# 负数怎么通过补码计算原码:补码取反加1,补码的补码就是原码
        	# 此处有一个规律:
			# ~n = -(n+1)
        	# ~(num1^0xffffffff)=-((num1 ^ 0xffffffff)+1)
            result = -((num1 ^ 0xffffffff)+1)  # 取反加1添负号
        return result

s = Solution()
res = s.Add(12, 8)
print(res)
发布了56 篇原创文章 · 获赞 1 · 访问量 1688

猜你喜欢

转载自blog.csdn.net/weixin_44549556/article/details/104508588
今日推荐