Commit 1492456e authored by Mark Dickinson's avatar Mark Dickinson

Issue #11144: Fix corner cases where float-to-int conversion unnecessarily returned a long.

parent 50777650
...@@ -52,6 +52,48 @@ class GeneralFloatCases(unittest.TestCase): ...@@ -52,6 +52,48 @@ class GeneralFloatCases(unittest.TestCase):
float('.' + '1'*1000) float('.' + '1'*1000)
float(unicode('.' + '1'*1000)) float(unicode('.' + '1'*1000))
def check_conversion_to_int(self, x):
"""Check that int(x) has the correct value and type, for a float x."""
n = int(x)
if x >= 0.0:
# x >= 0 and n = int(x) ==> n <= x < n + 1
self.assertLessEqual(n, x)
self.assertLess(x, n + 1)
else:
# x < 0 and n = int(x) ==> n >= x > n - 1
self.assertGreaterEqual(n, x)
self.assertGreater(x, n - 1)
# Result should be an int if within range, else a long.
if -sys.maxint-1 <= n <= sys.maxint:
self.assertEqual(type(n), int)
else:
self.assertEqual(type(n), long)
# Double check.
self.assertEqual(type(int(n)), type(n))
def test_conversion_to_int(self):
# Check that floats within the range of an int convert to type
# int, not long. (issue #11144.)
boundary = float(sys.maxint + 1)
epsilon = 2**-sys.float_info.mant_dig * boundary
# These 2 floats are either side of the positive int/long boundary on
# both 32-bit and 64-bit systems.
self.check_conversion_to_int(boundary - epsilon)
self.check_conversion_to_int(boundary)
# These floats are either side of the negative long/int boundary on
# 64-bit systems...
self.check_conversion_to_int(-boundary - 2*epsilon)
self.check_conversion_to_int(-boundary)
# ... and these ones are either side of the negative long/int
# boundary on 32-bit systems.
self.check_conversion_to_int(-boundary - 1.0)
self.check_conversion_to_int(-boundary - 1.0 + 2*epsilon)
@test_support.run_with_locale('LC_NUMERIC', 'fr_FR', 'de_DE') @test_support.run_with_locale('LC_NUMERIC', 'fr_FR', 'de_DE')
def test_float_with_comma(self): def test_float_with_comma(self):
# set locale to something that doesn't use '.' for the decimal point # set locale to something that doesn't use '.' for the decimal point
......
...@@ -9,6 +9,10 @@ What's New in Python 2.7.2? ...@@ -9,6 +9,10 @@ What's New in Python 2.7.2?
Core and Builtins Core and Builtins
----------------- -----------------
- Issue #11144: Ensure that int(a_float) returns an int whenever possible.
Previously, there were some corner cases where a long was returned even
though the result was within the range of an int.
- Issue #11675: multiprocessing.[Raw]Array objects created from an integer size - Issue #11675: multiprocessing.[Raw]Array objects created from an integer size
are now zeroed on creation. This matches the behaviour specified by the are now zeroed on creation. This matches the behaviour specified by the
documentation. documentation.
......
...@@ -1035,14 +1035,17 @@ float_trunc(PyObject *v) ...@@ -1035,14 +1035,17 @@ float_trunc(PyObject *v)
* happens if the double is too big to fit in a long. Some rare * happens if the double is too big to fit in a long. Some rare
* systems raise an exception then (RISCOS was mentioned as one, * systems raise an exception then (RISCOS was mentioned as one,
* and someone using a non-default option on Sun also bumped into * and someone using a non-default option on Sun also bumped into
* that). Note that checking for >= and <= LONG_{MIN,MAX} would * that). Note that checking for <= LONG_MAX is unsafe: if a long
* still be vulnerable: if a long has more bits of precision than * has more bits of precision than a double, casting LONG_MAX to
* a double, casting MIN/MAX to double may yield an approximation, * double may yield an approximation, and if that's rounded up,
* and if that's rounded up, then, e.g., wholepart=LONG_MAX+1 would * then, e.g., wholepart=LONG_MAX+1 would yield true from the C
* yield true from the C expression wholepart<=LONG_MAX, despite * expression wholepart<=LONG_MAX, despite that wholepart is
* that wholepart is actually greater than LONG_MAX. * actually greater than LONG_MAX. However, assuming a two's complement
* machine with no trap representation, LONG_MIN will be a power of 2 (and
* hence exactly representable as a double), and LONG_MAX = -1-LONG_MIN, so
* the comparisons with (double)LONG_MIN below should be safe.
*/ */
if (LONG_MIN < wholepart && wholepart < LONG_MAX) { if ((double)LONG_MIN <= wholepart && wholepart < -(double)LONG_MIN) {
const long aslong = (long)wholepart; const long aslong = (long)wholepart;
return PyInt_FromLong(aslong); return PyInt_FromLong(aslong);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment