1.概览
在计算机中,加减乘除运算由CPU实现,每次运算均需要将数据加载到内存中,处理后再将结果写入内存里。由于计算机只认识0和1,所以所有运算都是以二进制形式进行的。两个数的加法是直接用两者的补码相加,用补码的原因是:1. 将正数的符号位改为1来表示负数,这样的求和结果是不正确的;2. 正数的原码、反码、补码是相同的。
2.正数的原码、反码、补码
以8位CPU为例:
+1的原码、反码、补码均为:0000 0001,其中最前面的一位是标志位(0表示正数,1表示负数)。
这里补充一下,8位有符号二进制正数的范围是:0000 0001(
2
0
=
1
\ 2^{0} = 1
20=1)~ 0111 1111(
2
7
−
1
=
127
\ 2^{7}-1=127
27−1=127)。另外,试想一下,如果最大值0111 1111再加1会怎样呢?结果是1000 0000,表示的是-0即-128(-127-1=1000 0001+1111 1111=11000 0000=-128(所以,如果要输出正确的结果,就必须要进行扩位,否则就会发生溢出问题));8位有符号二进制负数的范围是1111 1111(
−
(
2
7
−
1
)
=
−
127
\ -( 2^{7}-1)=-127
−(27−1)=−127)~1000 0001(
−
(
2
0
)
=
−
1
\ -(2^{0} )= -1
−(20)=−1);还有两个特殊的0,+0(0000 0000)和-0(1000 0000)。
3.负数的原码、反码、补码
以8位CPU为例:
-1的原码为:1000 0001
-1的反码(除标志位外,按位取反~)为:1111 1110
-1的补码(反码+1)为:1111 1111
所有的二进制加法都是以补码的形式进行的!
4.用python来模拟计算机的加减法过程
4.1加法
class TailRecursive(object):
"""
tail_recursive decorator based on Kay Schluehr's recipe
http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/496691
with improvements by me and George Sakkis.
"""
def __init__(self, func):
self.func = func
self.firstcall = True
self.CONTINUE = object() # sentinel
def __call__(self, *args, **kwd):
CONTINUE = self.CONTINUE
if self.firstcall:
func = self.func
self.firstcall = False
try:
while True:
result = func(*args, **kwd)
if result is CONTINUE: # update arguments
args, kwd = self.argskwd
else: # last call
return result
finally:
self.firstcall = True
else: # return the arguments of the tail call
self.argskwd = args, kwd
return CONTINUE
# recursion version
# @TailRecursive
def add(a, b):
if b == 0:
return a
sum = a ^ b # xor implements addition which not including carry
carry = (a & b) << 1 # carry
return add(sum, carry)
print(add(3, 3))
"""
tail recursion -> iteration
you can also use the decorator:
@TailRecursive
"""
# iteration version
def add2(a, b):
sum = 0
carry = 0
while(b):
sum = a ^ b
carry = (a & b) << 1
a = sum
b = carry
return a
4.2减法
以下代码在进位的时候会一直增大,跳不出循环,有问题,有小伙伴能解决的嘛?,代码参考
def sub(a, b):
if a >= b:
return add2(a, add2(~b,1))
else:
return add2(~add2(b, add2(~a, 1)), 1)
print(sub(3, 1))
----2021.4.1更新----
上面这个代码不能跳出循环的根本原因是:python中的int数据的位数是不固定的,所以不想c++有溢出操作。那我们的解决思路就是:使用& 0xFFFFFFFF
来限制数据的范围到32位。此外,32位的最大正数为0x7FFFFFFF
,即:
2
31
−
1
=
2147483647
\ 2^{31}-1=2147483647
231−1=2147483647,如果输出
x
\ x
x 大于这个值,例如:0x080000000
,即:
2
31
=
2147483648
\ 2^{31}=2147483648
231=2147483648,则先做0x080000000 & 0xFFFFFFFF=0x07FFFFFFF
,然后再做~0x07FFFFFFF=0xF80000000
,即对应了最小负数
−
2
32
=
−
2147483648
\ -2^{32}=-2147483648
−232=−2147483648,这样便完美解决了溢出问题。下面是改进的代码:
# addition iteration
def add2(a, b):
max = 0x7FFFFFFF # the max positive number
mask = 0xFFFFFFFF # the mask to limit the number to 32bit
sum = 0
carry = 0
while(b):
sum = a ^ b & mask
carry = (a & b) << 1 & mask
a = sum
b = carry
return a if a <= max else ~(a & mask) # thanks for tong tong
print(add2(-12, -8))
def sub(a, b):
if a >= b:
return add2(a, add2(~b,1))
else:
return add2(~add2(b, add2(~a, 1)), 1)
print(sub(3, 1))