[BUG] IMO, but no opinions? Uncle Tim? was: int(float(sys.maxint)) buglet ?

Bengt Richter bokr at oz.net
Tue Dec 7 15:54:19 EST 2004


On Mon, 6 Dec 2004 10:30:06 -0500, Tim Peters <tim.peters at gmail.com> wrote:

>[Bengt Richter]
>> Peculiar boundary cases:
>> 
>> >>> 2.0**31-1.0
>> 2147483647.0
>> >>> int(2147483647.0)
>> 2147483647L
>> >>> int(2147483647L )
>> 2147483647
>> >>>
>> >>> -2.0**31
>> -2147483648.0
>> >>> int(-2147483648.0)
>> -2147483648L
>> >>> int(-2147483648L )
>> -2147483648
>> 
>> some kind of one-off error?
>
>It would help if you were explicit about what you think "the error"
>is.  I see a correct result in all cases there.
>
>Is it just that sometimes
>
>    int(a_float)
>
>returns a Python long when
>
>    int(a_long_with_the_same_value_as_that_float)
>
>returns a Python int?  If so, that's not a bug -- there's no promise
>anywhere, e.g., that Python will return an int whenever it's
>physically possible to do so.
Ok, I understand the expediency of that policy, but what is now the meaning
of int, in that case? Is it now just a vestigial artifact on the way to
transparent unification of int and long to a single integer type?

Promises or not, ISTM that if int->float succeeds in preserving all significant bits, then
then a following float->int should also succeed without converting to long.

>
>Python used to return a (short) int in all cases above, but that lead
>to problems on some oddball systems.  See the comments for float_int()
>in floatobject.c for more detail.  Slowing float_int() to avoid those
>problems while returning a short int whenever physically possible is a
>tradeoff I would oppose.

The 2.3.2 source snippet in floatobject.c :
--------------
static PyObject *
float_int(PyObject *v)
{
	double x = PyFloat_AsDouble(v);
	double wholepart;	/* integral portion of x, rounded toward 0 */

	(void)modf(x, &wholepart);
	/* Try to get out cheap if this fits in a Python int.  The attempt
	 * to cast to long must be protected, as C doesn't define what
	 * happens if the double is too big to fit in a long.  Some rare
	 * systems raise an exception then (RISCOS was mentioned as one,
	 * and someone using a non-default option on Sun also bumped into
	 * that).  Note that checking for >= and <= LONG_{MIN,MAX} would
	 * still be vulnerable:  if a long has more bits of precision than
	 * a double, casting MIN/MAX to double may yield an approximation,
	 * and if that's rounded up, then, e.g., wholepart=LONG_MAX+1 would
	 * yield true from the C expression wholepart<=LONG_MAX, despite
	 * that wholepart is actually greater than LONG_MAX.
	 */
	if (LONG_MIN < wholepart && wholepart < LONG_MAX) {
		const long aslong = (long)wholepart;
		return PyInt_FromLong(aslong);
	}
	return PyLong_FromDouble(wholepart);
}
--------------

But this is apparently accessed through a table of pointers, so would you oppose
an auto-configuration that one time tested whether
int(float(sys.maxint))==sys.maxint and int(float(-sys.maxint-1))==-sys.maxint-1
(assuming that's sufficient, of which I'm not 100% sure ;-) and if so switched
the pointer to a version that tested if(LONG_MIN <= wholepart && wholepart<=LONG_MAX)
instead of the safe-for-some-obscure-system version?

Of course, if int isn't all that meaningful any more, I guess the problem can be moved to the
ctypes module, if that gets included amongst the batteries ;-)

Regards,
Bengt Richter



More information about the Python-list mailing list