原码反码补码、二进制加减法知识点-python

1.概览

在计算机中,加减乘除运算由CPU实现,每次运算均需要将数据加载到内存中,处理后再将结果写入内存里。由于计算机只认识01,所以所有运算都是以二进制形式进行的。两个数的加法是直接用两者的补码相加,用补码的原因是: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  271=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  (271)=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  2311=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))

你可能感兴趣的:(python每日探索)