Skip to content

Commit 1f2d47c

Browse files
committed
Implement PyLong_FromLong separately from _PyLong_FromSTwoDigits to allow for 15 bit digits on 64 bit machines.
1 parent ed2a430 commit 1f2d47c

File tree

1 file changed

+45
-6
lines changed

1 file changed

+45
-6
lines changed

Objects/longobject.c

+45-6
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ medium_value(PyLongObject *x)
4040
#define IS_SMALL_INT(ival) (-NSMALLNEGINTS <= (ival) && (ival) < NSMALLPOSINTS)
4141
#define IS_SMALL_UINT(ival) ((ival) < NSMALLPOSINTS)
4242

43+
/* To be valid the type of x must cover -PyLong_BASE to +PyLong_BASE.
44+
int, long, Py_ssize_t are all ok */
4345
#define IS_MEDIUM_INT(x) (((twodigits)x)+PyLong_MASK <= 2*PyLong_MASK)
4446

4547
static PyObject *
@@ -195,9 +197,10 @@ _PyLong_FromLarge(stwodigits ival)
195197
abs_ival = (twodigits)ival;
196198
sign = 1;
197199
}
198-
/* Loop to determine number of digits */
199-
twodigits t = abs_ival;
200-
Py_ssize_t ndigits = 0;
200+
/* Must be at least two digits */
201+
assert(abs_ival >> PyLong_SHIFT != 0);
202+
twodigits t = abs_ival >> (PyLong_SHIFT *2);
203+
Py_ssize_t ndigits = 2;
201204
while (t) {
202205
++ndigits;
203206
t >>= PyLong_SHIFT;
@@ -251,8 +254,44 @@ _PyLong_Negate(PyLongObject **x_p)
251254
PyObject *
252255
PyLong_FromLong(long ival)
253256
{
254-
Py_BUILD_ASSERT(sizeof(stwodigits) >= sizeof(long));
255-
return _PyLong_FromSTwoDigits(ival);
257+
if (IS_SMALL_INT(ival)) {
258+
return get_small_int((sdigit)ival);
259+
}
260+
unsigned long abs_ival;
261+
int sign;
262+
if (ival < 0) {
263+
/* negate: can't write this as abs_ival = -ival since that
264+
invokes undefined behaviour when ival is LONG_MIN */
265+
abs_ival = 0U-(twodigits)ival;
266+
sign = -1;
267+
}
268+
else {
269+
abs_ival = (unsigned long)ival;
270+
sign = 1;
271+
}
272+
/* Fast path for single-digit ints */
273+
if (!(abs_ival >> PyLong_SHIFT)) {
274+
return _PyLong_FromMedium((sdigit)ival);
275+
}
276+
/* Must be at least two digits */
277+
unsigned long t = abs_ival >> (PyLong_SHIFT *2);
278+
Py_ssize_t ndigits = 2;
279+
while (t) {
280+
++ndigits;
281+
t >>= PyLong_SHIFT;
282+
}
283+
PyLongObject *v = _PyLong_New(ndigits);
284+
if (v != NULL) {
285+
digit *p = v->ob_digit;
286+
Py_SET_SIZE(v, ndigits * sign);
287+
t = abs_ival;
288+
while (t) {
289+
*p++ = Py_SAFE_DOWNCAST(
290+
t & PyLong_MASK, unsigned long, digit);
291+
t >>= PyLong_SHIFT;
292+
}
293+
}
294+
return (PyObject *)v;
256295
}
257296

258297
#define PYLONG_FROM_UINT(INT_TYPE, ival) \
@@ -3554,7 +3593,7 @@ long_mul(PyLongObject *a, PyLongObject *b)
35543593
/* fast path for single-digit multiplication */
35553594
if (IS_MEDIUM_VALUE(a) && IS_MEDIUM_VALUE(b)) {
35563595
stwodigits v = medium_value(a) * medium_value(b);
3557-
return PyLong_FromLongLong((long long)v);
3596+
return _PyLong_FromSTwoDigits(v);
35583597
}
35593598

35603599
z = k_mul(a, b);

0 commit comments

Comments
 (0)