Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does int(maxint) give a long, but int(int(maxint)) give an int? Is this a NumPy bug?

Pretty self-explanatory (I'm on Windows):

>>> import sys, numpy
>>> a = numpy.int_(sys.maxint)
>>> int(a).__class__
<type 'long'>
>>> int(int(a)).__class__
<type 'int'>

Why does calling int once give me a long, whereas calling it twice gives me an int?

Is this a bug or a feature?

like image 535
user541686 Avatar asked May 24 '17 10:05

user541686


People also ask

What is the maximum size of an integer in Python 3?

In Python3, int has no max limit. Python2 has two integer types, int and long , but Python3 has only int . int in Python3 is equivalent to long in Python2, and there is no max limit.

How big can Python int be?

These represent numbers in the range -2147483648 through 2147483647. (The range may be larger on machines with a larger natural word size, but not smaller.)


1 Answers

This question is specific to Numpy and Python 2. In Python 3 there are no separate int and long types.

The behaviour happens due to an off-by-one error in numpy. int(x) with one argument converts x to number by calling PyNumber_Int(x). PyNumber_Int then specifically takes the path for int subclasses, as int64 returned by numpy.int_ is a subclass of int:

m = o->ob_type->tp_as_number;
if (m && m->nb_int) { /* This should include subclasses of int */
    /* Classic classes always take this branch. */
    PyObject *res = m->nb_int(o);
    if (res && (!PyInt_Check(res) && !PyLong_Check(res))) {
        PyErr_Format(PyExc_TypeError,
                     "__int__ returned non-int (type %.200s)",
                     res->ob_type->tp_name);
        Py_DECREF(res);
        return NULL;
    }
    return res;
}

Now, for this code calls a->ob_type->tp_as_number->nb_int, which is implemented in numpy/core/src/umath/scalarmath.c.src. This is the location for code that is parametrized for different types; this one for <typename>_int method that is used to fill the nb_int method slot. It has the following off-by one if there:

if(LONG_MIN < x && x < LONG_MAX)
    return PyInt_FromLong(x);

both operators should be <= instead. With < there, neither LONG_MIN nor LONG_MAX pass the condition and they're instead are converted into a PyLong at line 1432:

return @func@(x);

with @func@ being replaced by PyLong_FromLongLong in the case of int_. Thus, long(sys.maxint) is returned.

Now, as the sys.maxint is still representable by int, int(long(sys.maxint)) returns an int; likewise int(sys.maxint + 1) returns a long.