在计算机中,加减乘除运算由CPU实现,每次运算均需要将数据加载到内存中,处理后再将结果写入内存里。由于计算机只认识0和1,所以所有运算都是以二进制形式进行的。两个数的加法是直接用两者的补码相加,用补码的原因是:1. 将正数的符号位改为1来表示负数,这样的求和结果是不正确的;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)。
以8位CPU为例:
-1的原码为:1000 0001
-1的反码(除标志位外,按位取反~)为:1111 1110
-1的补码(反码+1)为:1111 1111
所有的二进制加法都是以补码的形式进行的!
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
以下代码在进位的时候会一直增大,跳不出循环,有问题,有小伙伴能解决的嘛?,代码参考
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))