简介:

本节主要目的要基本了解python中整数加减运算的实现,在此之前要先了解python中整数实现方式可以先查看(一)之后再看本节。

注:这里说明一下,下面介绍均已8个比特位举例。那么ob_digit 每一个元素我们认定最多只能占7位。从源码中类推过来,这样是为了防止两数相加溢出问题,比如255+255 这时8位肯定放不下了,只占7位最多127+127=254。

1.加法运算

在了解了整数实现之后,我们在同一文件下(longobject.c)可以发现long_add函数实现了两整数相加的运算,那么接下来我们就来一步一步分析其实现:

static PyObject *
long_add(PyLongObject *a, PyLongObject *b)
{
    PyLongObject *z;

    CHECK_BINOP(a, b);

    if (Py_ABS(Py_SIZE(a)) <= 1 && Py_ABS(Py_SIZE(b)) <= 1) {
        return PyLong_FromLong(MEDIUM_VALUE(a) + MEDIUM_VALUE(b));
    }
    if (Py_SIZE(a) < 0) {
        if (Py_SIZE(b) < 0) {
            z = x_add(a, b);
            if (z != NULL) {
                /* x_add received at least one multiple-digit int,
                   and thus z must be a multiple-digit int.
                   That also means z is not an element of
                   small_ints, so negating it in-place is safe. */
                assert(Py_REFCNT(z) == 1);
                Py_SIZE(z) = -(Py_SIZE(z));
            }
        }
        else
            z = x_sub(b, a);
    }
    else {
        if (Py_SIZE(b) < 0)
            z = x_sub(a, b);
        else
            z = x_add(a, b);
    }
    return (PyObject *)z;
}

从上面这段代码中首先可以看到当整数ob_digit长度<=1的时候是通过PyLong_FromLong(MEDIUM_VALUE(a) + MEDIUM_VALUE(b))来求值的,那么接下来就简单看一下它的实现方式:

python算加法 如何用python计算加法_python算加法

先将a,b的值通过C相加之后再由 PyLong_FromLong转成Python的整数对象,那么接下来我们就先看一下它是如何转换的。

PyObject *
PyLong_FromLong(long ival)
{
    PyLongObject *v;
    unsigned long abs_ival;
    unsigned long t;  /* unsigned so >> doesn't propagate sign bit */
    int ndigits = 0;
    int sign;

    CHECK_SMALL_INT(ival);
    
    /* 下面这个判断通过传进来的C整数,确定ob_size值 */
    if (ival < 0) {
        /* negate: can't write this as abs_ival = -ival since that
           invokes undefined behaviour when ival is LONG_MIN */
        abs_ival = 0U-(unsigned long)ival;
        sign = -1;
    }
    else {
        abs_ival = (unsigned long)ival;
        sign = ival == 0 ? 0 : 1;
    }
    
    /* Fast path for single-digit ints
        这里因为我们通过8个比特位举例,我们先认定PyLong_SHIFT=7,那么右移7位为0了则条件成立
        这样就确保abs_ival最大占用<=7个比特位时,执行此处转换。
        举个例子:abs_ival=8,二进制0000 1000,>>7=0000 0000,
        最终返回一个ob_size=1,ob_digit=[8]的PyObject对象
     */
    if (!(abs_ival >> PyLong_SHIFT)) {
        v = _PyLong_New(1);
        if (v) {
            Py_SIZE(v) = sign;
            v->ob_digit[0] = Py_SAFE_DOWNCAST(
                abs_ival, unsigned long, digit);
        }
        return (PyObject*)v;
    }

    /* Larger numbers: loop to determine number of digits
        这里就是当abs_ival会占满8个比特位,我们先认定ob_digit每一个元素最多只能占用7位
        此时就要进行转换,举个例子:t = abs_ival = 200 = 1100 1000
        执行结果返回一个ob_size = 2, ob_digit = [72, 1] 的对象
     */
    t = abs_ival;
    while (t) {
        ++ndigits;
        t >>= PyLong_SHIFT;
    }
    // 此处ndigits = 2
    v = _PyLong_New(ndigits);
    if (v != NULL) {
        digit *p = v->ob_digit;
        Py_SIZE(v) = ndigits*sign; // 此处v的ob_size=2
        t = abs_ival; //t重新赋值200
        while (t) {
            // 第一次 t & PyLong_MASK = 200 & 127 = 0100 1000 = 72
            // 第二次 t & PyLong_MASK = 1 & 127 = 0000 0001 = 1
            *p++ = Py_SAFE_DOWNCAST(
                t & PyLong_MASK, unsigned long, digit);
            t >>= PyLong_SHIFT;
        }
    }
    return (PyObject *)v;
}

通过上述介绍,我们应该可以清楚ob_size的绝对值小于等于1的Python整数是如何相加的。

那么接下来看其他情况:根据两个数正负来判断是执行x_add函数还是执行x_sub函数,我们均已举例说明的方式查看。

例1:a = -200, b = -600
解析:此时当执行x_add函数
分别将a,b转为python对象 
a = {ob_size: 2, ob_digit: [72, 1]}
b = {ob_size: 2, ob_digit: [88, 4]}
static PyLongObject *
x_add(PyLongObject *a, PyLongObject *b)
{
    // size_a = size_b = 2
    Py_ssize_t size_a = Py_ABS(Py_SIZE(a)), size_b = Py_ABS(Py_SIZE(b));
    PyLongObject *z;
    Py_ssize_t i;
    digit carry = 0;

    /* Ensure a is the larger of the two:
        这里会始终保持a的ob_size>b的ob_size
     */
    if (size_a < size_b) {
        { PyLongObject *temp = a; a = b; b = temp; }
        { Py_ssize_t size_temp = size_a;
            size_a = size_b;
            size_b = size_temp; }
    }
    z = _PyLong_New(size_a+1);
    if (z == NULL)
        return NULL;
    /* 下述for循环将两数逐位相加 */
    for (i = 0; i < size_b; ++i) {
        /* 第一轮:carry = 160
           第二轮:carry = 1 + 4 + 1 = 6
         */
        carry += a->ob_digit[i] + b->ob_digit[i];
        /* 第一轮:z->ob_digit[0] = 160 & 127 = 32
           第二轮:z->ob_digit[1] = 6 & 127 = 6
         */
        z->ob_digit[i] = carry & PyLong_MASK;
        /* 第一轮:carry = 160 >> 7 = 1
           第二轮:carry = 6 >> 7 = 0
         */
        carry >>= PyLong_SHIFT;
    }
    for (; i < size_a; ++i) {
        carry += a->ob_digit[i];
        z->ob_digit[i] = carry & PyLong_MASK;
        carry >>= PyLong_SHIFT;
    }
    z->ob_digit[i] = carry;  // z->ob_digit[2] = 0
    return long_normalize(z);
}
最终结果:z = {ob_size: -2, ob_digit: [32, 6]} = -(32*128**0 + 6*128**1) = -800
例2:a = 200, b = -600
解析:此时执行x_sub(a, b)
将a,b分别转成python对象
a = {ob_size: 2, ob_digit: [72, 1]}
b = {ob_size: -2, ob_digit: [88, 4]}
将a和b带入x_sub
static PyLongObject *
x_sub(PyLongObject *a, PyLongObject *b)
{
    // size_a = size_b = 2
    Py_ssize_t size_a = Py_ABS(Py_SIZE(a)), size_b = Py_ABS(Py_SIZE(b));
    PyLongObject *z;
    Py_ssize_t i;
    int sign = 1;
    digit borrow = 0;

    /* Ensure a is the larger of the two:
        此处同x_add一致
     */
    if (size_a < size_b) {
        sign = -1;
        { PyLongObject *temp = a; a = b; b = temp; }
        { Py_ssize_t size_temp = size_a;
            size_a = size_b;
            size_b = size_temp; }
    }
    else if (size_a == size_b) {
        /* 查找a和b最高位不相同的位置,相同位置的直接忽略,本例均不相同 */
        i = size_a;
        while (--i >= 0 && a->ob_digit[i] == b->ob_digit[i])
            ;
        /* 判断两数绝对值是否相等,相等直接返回0 */
        if (i < 0)
            return (PyLongObject *)PyLong_FromLong(0);
        /* 确保a变量绝对值最大 */
        if (a->ob_digit[i] < b->ob_digit[i]) {
            sign = -1;  // 标志结果为负数
            { PyLongObject *temp = a; a = b; b = temp; }
        }
        size_a = size_b = i+1;
    }
    // 执行至此,a和b已交换
    z = _PyLong_New(size_a);
    if (z == NULL)
        return NULL;
    for (i = 0; i < size_b; ++i) {
        /* The following assumes unsigned arithmetic
           works module 2**N for some N>PyLong_SHIFT. */
        /* 第一轮:borrow = 88 - 72 - 0 = 16 
           第二轮:borrow = 4 - 1 - 0 = 3
        */
        borrow = a->ob_digit[i] - b->ob_digit[i] - borrow;
        /* 第一轮:z->ob_digit[0] = 16 & 127 = 16 
           第二轮:z->ob_digit[1] = 3 & 127 = 3
        */
        z->ob_digit[i] = borrow & PyLong_MASK;
        /* 第一轮:borrow = 16 >> 7 = 0
           第二轮:borrow = 3 >> 7 = 0
        */
        borrow >>= PyLong_SHIFT;
        /* 第一轮:borrow = 0 & 1 = 0
           第二轮:borrow = 0 & 1 = 0
        */
        borrow &= 1; /* Keep only one sign bit */
    }
    for (; i < size_a; ++i) {
        borrow = a->ob_digit[i] - borrow;
        z->ob_digit[i] = borrow & PyLong_MASK;
        borrow >>= PyLong_SHIFT;
        borrow &= 1; /* Keep only one sign bit */
    }
    assert(borrow == 0);
    if (sign < 0) {  // 本例此时成立
        Py_SIZE(z) = -Py_SIZE(z);
    }
    return long_normalize(z);
}
最终结果:z = {ob_size: -2, ob_digit: [16, 3]} = -(16*128**0 + 3*128**1) = -400

2.减法运算

python算加法 如何用python计算加法_开发语言_02

 减法运算原理其实跟加法差不多的,就不在赘述了。