summaryrefslogtreecommitdiffstats
path: root/Lib/test/test_long.py
diff options
context:
space:
mode:
Diffstat (limited to 'Lib/test/test_long.py')
-rw-r--r--Lib/test/test_long.py143
1 files changed, 108 insertions, 35 deletions
diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py
index 3ebc1f7..3f9c1e2 100644
--- a/Lib/test/test_long.py
+++ b/Lib/test/test_long.py
@@ -43,6 +43,53 @@ DBL_MIN_EXP = sys.float_info.min_exp
DBL_MANT_DIG = sys.float_info.mant_dig
DBL_MIN_OVERFLOW = 2**DBL_MAX_EXP - 2**(DBL_MAX_EXP - DBL_MANT_DIG - 1)
+
+# Pure Python version of correctly-rounded integer-to-float conversion.
+def int_to_float(n):
+ """
+ Correctly-rounded integer-to-float conversion.
+
+ """
+ # Constants, depending only on the floating-point format in use.
+ # We use an extra 2 bits of precision for rounding purposes.
+ PRECISION = sys.float_info.mant_dig + 2
+ SHIFT_MAX = sys.float_info.max_exp - PRECISION
+ Q_MAX = 1 << PRECISION
+ ROUND_HALF_TO_EVEN_CORRECTION = [0, -1, -2, 1, 0, -1, 2, 1]
+
+ # Reduce to the case where n is positive.
+ if n == 0:
+ return 0.0
+ elif n < 0:
+ return -int_to_float(-n)
+
+ # Convert n to a 'floating-point' number q * 2**shift, where q is an
+ # integer with 'PRECISION' significant bits. When shifting n to create q,
+ # the least significant bit of q is treated as 'sticky'. That is, the
+ # least significant bit of q is set if either the corresponding bit of n
+ # was already set, or any one of the bits of n lost in the shift was set.
+ shift = n.bit_length() - PRECISION
+ q = n << -shift if shift < 0 else (n >> shift) | bool(n & ~(-1 << shift))
+
+ # Round half to even (actually rounds to the nearest multiple of 4,
+ # rounding ties to a multiple of 8).
+ q += ROUND_HALF_TO_EVEN_CORRECTION[q & 7]
+
+ # Detect overflow.
+ if shift + (q == Q_MAX) > SHIFT_MAX:
+ raise OverflowError("integer too large to convert to float")
+
+ # Checks: q is exactly representable, and q**2**shift doesn't overflow.
+ assert q % 4 == 0 and q // 4 <= 2**(sys.float_info.mant_dig)
+ assert q * 2**shift <= sys.float_info.max
+
+ # Some circularity here, since float(q) is doing an int-to-float
+ # conversion. But here q is of bounded size, and is exactly representable
+ # as a float. In a low-level C-like language, this operation would be a
+ # simple cast (e.g., from unsigned long long to double).
+ return math.ldexp(float(q), shift)
+
+
# pure Python version of correctly-rounded true division
def truediv(a, b):
"""Correctly-rounded true division for integers."""
@@ -83,7 +130,7 @@ class LongTest(unittest.TestCase):
# The sign of the number is also random.
def getran(self, ndigits):
- self.assertTrue(ndigits > 0)
+ self.assertGreater(ndigits, 0)
nbits_hi = ndigits * SHIFT
nbits_lo = nbits_hi - SHIFT + 1
answer = 0
@@ -275,20 +322,13 @@ class LongTest(unittest.TestCase):
"".join("0123456789abcdef"[i] for i in digits)
def check_format_1(self, x):
- for base, mapper in (8, oct), (10, repr), (16, hex):
+ for base, mapper in (2, bin), (8, oct), (10, str), (10, repr), (16, hex):
got = mapper(x)
expected = self.slow_format(x, base)
msg = Frm("%s returned %r but expected %r for %r",
mapper.__name__, got, expected, x)
self.assertEqual(got, expected, msg)
self.assertEqual(int(got, 0), x, Frm('int("%s", 0) != %r', got, x))
- # str() has to be checked a little differently since there's no
- # trailing "L"
- got = str(x)
- expected = self.slow_format(x, 10)
- msg = Frm("%s returned %r but expected %r for %r",
- mapper.__name__, got, expected, x)
- self.assertEqual(got, expected, msg)
def test_format(self):
for x in special:
@@ -367,6 +407,23 @@ class LongTest(unittest.TestCase):
return 1729
self.assertEqual(int(LongTrunc()), 1729)
+ def check_float_conversion(self, n):
+ # Check that int -> float conversion behaviour matches
+ # that of the pure Python version above.
+ try:
+ actual = float(n)
+ except OverflowError:
+ actual = 'overflow'
+
+ try:
+ expected = int_to_float(n)
+ except OverflowError:
+ expected = 'overflow'
+
+ msg = ("Error in conversion of integer {} to float. "
+ "Got {}, expected {}.".format(n, actual, expected))
+ self.assertEqual(actual, expected, msg)
+
@support.requires_IEEE_754
def test_float_conversion(self):
@@ -421,6 +478,22 @@ class LongTest(unittest.TestCase):
y = 2**p * 2**53
self.assertEqual(int(float(x)), y)
+ # Compare builtin float conversion with pure Python int_to_float
+ # function above.
+ test_values = [
+ int_dbl_max-1, int_dbl_max, int_dbl_max+1,
+ halfway-1, halfway, halfway + 1,
+ top_power-1, top_power, top_power+1,
+ 2*top_power-1, 2*top_power, top_power*top_power,
+ ]
+ test_values.extend(exact_values)
+ for p in range(-4, 8):
+ for x in range(-128, 128):
+ test_values.append(2**(p+53) + x)
+ for value in test_values:
+ self.check_float_conversion(value)
+ self.check_float_conversion(-value)
+
def test_float_overflow(self):
for x in -2.0, -1.0, 0.0, 1.0, 2.0:
self.assertEqual(float(int(x)), x)
@@ -473,11 +546,11 @@ class LongTest(unittest.TestCase):
def test_mixed_compares(self):
eq = self.assertEqual
- # We're mostly concerned with that mixing floats and longs does the
- # right stuff, even when longs are too large to fit in a float.
+ # We're mostly concerned with that mixing floats and ints does the
+ # right stuff, even when ints are too large to fit in a float.
# The safest way to check the results is to use an entirely different
# method, which we do here via a skeletal rational class (which
- # represents all Python ints, longs and floats exactly).
+ # represents all Python ints and floats exactly).
class Rat:
def __init__(self, value):
if isinstance(value, int):
@@ -792,21 +865,21 @@ class LongTest(unittest.TestCase):
def test_small_ints(self):
for i in range(-5, 257):
- self.assertTrue(i is i + 0)
- self.assertTrue(i is i * 1)
- self.assertTrue(i is i - 0)
- self.assertTrue(i is i // 1)
- self.assertTrue(i is i & -1)
- self.assertTrue(i is i | 0)
- self.assertTrue(i is i ^ 0)
- self.assertTrue(i is ~~i)
- self.assertTrue(i is i**1)
- self.assertTrue(i is int(str(i)))
- self.assertTrue(i is i<<2>>2, str(i))
+ self.assertIs(i, i + 0)
+ self.assertIs(i, i * 1)
+ self.assertIs(i, i - 0)
+ self.assertIs(i, i // 1)
+ self.assertIs(i, i & -1)
+ self.assertIs(i, i | 0)
+ self.assertIs(i, i ^ 0)
+ self.assertIs(i, ~~i)
+ self.assertIs(i, i**1)
+ self.assertIs(i, int(str(i)))
+ self.assertIs(i, i<<2>>2, str(i))
# corner cases
i = 1 << 70
- self.assertTrue(i - i is 0)
- self.assertTrue(0 * i is 0)
+ self.assertIs(i - i, 0)
+ self.assertIs(0 * i, 0)
def test_bit_length(self):
tiny = 1e-10
@@ -853,7 +926,7 @@ class LongTest(unittest.TestCase):
got = round(k+offset, -1)
expected = v+offset
self.assertEqual(got, expected)
- self.assertTrue(type(got) is int)
+ self.assertIs(type(got), int)
# larger second argument
self.assertEqual(round(-150, -2), -200)
@@ -892,7 +965,7 @@ class LongTest(unittest.TestCase):
got = round(10**k + 324678, -3)
expect = 10**k + 325000
self.assertEqual(got, expect)
- self.assertTrue(type(got) is int)
+ self.assertIs(type(got), int)
# nonnegative second argument: round(x, n) should just return x
for n in range(5):
@@ -900,7 +973,7 @@ class LongTest(unittest.TestCase):
x = random.randrange(-10000, 10000)
got = round(x, n)
self.assertEqual(got, x)
- self.assertTrue(type(got) is int)
+ self.assertIs(type(got), int)
for huge_n in 2**31-1, 2**31, 2**63-1, 2**63, 2**100, 10**100:
self.assertEqual(round(8979323, huge_n), 8979323)
@@ -909,7 +982,7 @@ class LongTest(unittest.TestCase):
x = random.randrange(-10000, 10000)
got = round(x)
self.assertEqual(got, x)
- self.assertTrue(type(got) is int)
+ self.assertIs(type(got), int)
# bad second argument
bad_exponents = ('brian', 2.0, 0j, None)
@@ -1114,15 +1187,15 @@ class LongTest(unittest.TestCase):
class myint(int):
pass
- self.assertTrue(type(myint.from_bytes(b'\x00', 'big')) is myint)
+ self.assertIs(type(myint.from_bytes(b'\x00', 'big')), myint)
self.assertEqual(myint.from_bytes(b'\x01', 'big'), 1)
- self.assertTrue(
- type(myint.from_bytes(b'\x00', 'big', signed=False)) is myint)
+ self.assertIs(
+ type(myint.from_bytes(b'\x00', 'big', signed=False)), myint)
self.assertEqual(myint.from_bytes(b'\x01', 'big', signed=False), 1)
- self.assertTrue(type(myint.from_bytes(b'\x00', 'little')) is myint)
+ self.assertIs(type(myint.from_bytes(b'\x00', 'little')), myint)
self.assertEqual(myint.from_bytes(b'\x01', 'little'), 1)
- self.assertTrue(type(myint.from_bytes(
- b'\x00', 'little', signed=False)) is myint)
+ self.assertIs(type(myint.from_bytes(
+ b'\x00', 'little', signed=False)), myint)
self.assertEqual(myint.from_bytes(b'\x01', 'little', signed=False), 1)
self.assertEqual(
int.from_bytes([255, 0, 0], 'big', signed=True), -65536)