mirror of
https://github.com/python/cpython.git
synced 2025-12-10 11:00:14 +00:00
divrem1 & long_format: found a clean way to factor divrem1 so that
long_format can reuse a scratch area for its repeated divisions (instead of malloc/free for every digit produced); speeds str(long)/repr(long).
This commit is contained in:
parent
c8a6b9b6d6
commit
212e614f60
1 changed files with 54 additions and 28 deletions
|
|
@ -15,7 +15,7 @@
|
||||||
static PyLongObject *long_normalize(PyLongObject *);
|
static PyLongObject *long_normalize(PyLongObject *);
|
||||||
static PyLongObject *mul1(PyLongObject *, wdigit);
|
static PyLongObject *mul1(PyLongObject *, wdigit);
|
||||||
static PyLongObject *muladd1(PyLongObject *, wdigit, wdigit);
|
static PyLongObject *muladd1(PyLongObject *, wdigit, wdigit);
|
||||||
static PyLongObject *divrem1(PyLongObject *, wdigit, digit *);
|
static PyLongObject *divrem1(PyLongObject *, digit, digit *);
|
||||||
static PyObject *long_format(PyObject *aa, int base, int addL);
|
static PyObject *long_format(PyObject *aa, int base, int addL);
|
||||||
|
|
||||||
static int ticker; /* XXX Could be shared with ceval? */
|
static int ticker; /* XXX Could be shared with ceval? */
|
||||||
|
|
@ -707,28 +707,44 @@ muladd1(PyLongObject *a, wdigit n, wdigit extra)
|
||||||
return long_normalize(z);
|
return long_normalize(z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Divide long pin, w/ size digits, by non-zero digit n, storing quotient
|
||||||
|
in pout, and returning the remainder. pin and pout point at the LSD.
|
||||||
|
It's OK for pin == pout on entry, which saves oodles of mallocs/frees in
|
||||||
|
long_format, but that should be done with great care since longs are
|
||||||
|
immutable. */
|
||||||
|
|
||||||
|
static digit
|
||||||
|
inplace_divrem1(digit *pout, digit *pin, int size, digit n)
|
||||||
|
{
|
||||||
|
twodigits rem = 0;
|
||||||
|
|
||||||
|
assert(n > 0 && n <= MASK);
|
||||||
|
pin += size;
|
||||||
|
pout += size;
|
||||||
|
while (--size >= 0) {
|
||||||
|
digit hi;
|
||||||
|
rem = (rem << SHIFT) + *--pin;
|
||||||
|
*--pout = hi = (digit)(rem / n);
|
||||||
|
rem -= hi * n;
|
||||||
|
}
|
||||||
|
return (digit)rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* Divide a long integer by a digit, returning both the quotient
|
/* Divide a long integer by a digit, returning both the quotient
|
||||||
(as function result) and the remainder (through *prem).
|
(as function result) and the remainder (through *prem).
|
||||||
The sign of a is ignored; n should not be zero. */
|
The sign of a is ignored; n should not be zero. */
|
||||||
|
|
||||||
static PyLongObject *
|
static PyLongObject *
|
||||||
divrem1(PyLongObject *a, wdigit n, digit *prem)
|
divrem1(PyLongObject *a, digit n, digit *prem)
|
||||||
{
|
{
|
||||||
int size = ABS(a->ob_size);
|
const int size = ABS(a->ob_size);
|
||||||
PyLongObject *z;
|
PyLongObject *z;
|
||||||
int i;
|
|
||||||
twodigits rem = 0;
|
|
||||||
|
|
||||||
assert(n > 0 && n <= MASK);
|
assert(n > 0 && n <= MASK);
|
||||||
z = _PyLong_New(size);
|
z = _PyLong_New(size);
|
||||||
if (z == NULL)
|
if (z == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
for (i = size; --i >= 0; ) {
|
*prem = inplace_divrem1(z->ob_digit, a->ob_digit, size, n);
|
||||||
rem = (rem << SHIFT) + a->ob_digit[i];
|
|
||||||
z->ob_digit[i] = (digit) (rem/n);
|
|
||||||
rem %= n;
|
|
||||||
}
|
|
||||||
*prem = (digit) rem;
|
|
||||||
return long_normalize(z);
|
return long_normalize(z);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -742,7 +758,7 @@ long_format(PyObject *aa, int base, int addL)
|
||||||
register PyLongObject *a = (PyLongObject *)aa;
|
register PyLongObject *a = (PyLongObject *)aa;
|
||||||
PyStringObject *str;
|
PyStringObject *str;
|
||||||
int i;
|
int i;
|
||||||
int size_a = ABS(a->ob_size);
|
const int size_a = ABS(a->ob_size);
|
||||||
char *p;
|
char *p;
|
||||||
int bits;
|
int bits;
|
||||||
char sign = '\0';
|
char sign = '\0';
|
||||||
|
|
@ -779,7 +795,7 @@ long_format(PyObject *aa, int base, int addL)
|
||||||
twodigits temp = a->ob_digit[0];
|
twodigits temp = a->ob_digit[0];
|
||||||
int bitsleft = SHIFT;
|
int bitsleft = SHIFT;
|
||||||
int rem;
|
int rem;
|
||||||
int last = abs(a->ob_size);
|
int last = size_a;
|
||||||
int basebits = 1;
|
int basebits = 1;
|
||||||
i = base;
|
i = base;
|
||||||
while ((i >>= 1) > 1)
|
while ((i >>= 1) > 1)
|
||||||
|
|
@ -814,6 +830,10 @@ long_format(PyObject *aa, int base, int addL)
|
||||||
/* Not 0, and base not a power of 2. Divide repeatedly by
|
/* Not 0, and base not a power of 2. Divide repeatedly by
|
||||||
base, but for speed use the highest power of base that
|
base, but for speed use the highest power of base that
|
||||||
fits in a digit. */
|
fits in a digit. */
|
||||||
|
int size = size_a;
|
||||||
|
digit *pin = a->ob_digit;
|
||||||
|
PyLongObject *scratch;
|
||||||
|
/* powbasw <- largest power of base that fits in a digit. */
|
||||||
digit powbase = base; /* powbase == base ** power */
|
digit powbase = base; /* powbase == base ** power */
|
||||||
int power = 1;
|
int power = 1;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
|
|
@ -823,23 +843,29 @@ long_format(PyObject *aa, int base, int addL)
|
||||||
powbase = (digit)newpow;
|
powbase = (digit)newpow;
|
||||||
++power;
|
++power;
|
||||||
}
|
}
|
||||||
|
|
||||||
Py_INCREF(a);
|
/* Get a scratch area for repeated division. */
|
||||||
|
scratch = _PyLong_New(size);
|
||||||
|
if (scratch == NULL) {
|
||||||
|
Py_DECREF(str);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Repeatedly divide by powbase. */
|
||||||
do {
|
do {
|
||||||
int ntostore = power;
|
int ntostore = power;
|
||||||
digit rem;
|
digit rem = inplace_divrem1(scratch->ob_digit,
|
||||||
PyLongObject *temp = divrem1(a, powbase, &rem);
|
pin, size, powbase);
|
||||||
Py_DECREF(a);
|
pin = scratch->ob_digit; /* no need to use a again */
|
||||||
if (temp == NULL) {
|
if (pin[size - 1] == 0)
|
||||||
Py_DECREF(str);
|
--size;
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
a = temp;
|
|
||||||
SIGCHECK({
|
SIGCHECK({
|
||||||
Py_DECREF(a);
|
Py_DECREF(scratch);
|
||||||
Py_DECREF(str);
|
Py_DECREF(str);
|
||||||
return NULL;
|
return NULL;
|
||||||
})
|
})
|
||||||
|
|
||||||
|
/* Break rem into digits. */
|
||||||
assert(ntostore > 0);
|
assert(ntostore > 0);
|
||||||
do {
|
do {
|
||||||
digit nextrem = (digit)(rem / base);
|
digit nextrem = (digit)(rem / base);
|
||||||
|
|
@ -851,10 +877,10 @@ long_format(PyObject *aa, int base, int addL)
|
||||||
--ntostore;
|
--ntostore;
|
||||||
/* Termination is a bit delicate: must not
|
/* Termination is a bit delicate: must not
|
||||||
store leading zeroes, so must get out if
|
store leading zeroes, so must get out if
|
||||||
a and rem are both 0 now. */
|
remaining quotient and rem are both 0. */
|
||||||
} while (ntostore && (a->ob_size || rem));
|
} while (ntostore && (size || rem));
|
||||||
} while (a->ob_size != 0);
|
} while (size != 0);
|
||||||
Py_DECREF(a);
|
Py_DECREF(scratch);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (base == 8) {
|
if (base == 8) {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue