简介:
本节主要目的要基本了解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))来求值的,那么接下来就简单看一下它的实现方式:

先将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) = -4002.减法运算

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
















