Index: Lib/test/test_long.py =================================================================== --- Lib/test/test_long.py (revision 88061) +++ Lib/test/test_long.py (working copy) @@ -5,6 +5,48 @@ import random import math +def long_to_float(n): + """Pure Python correctly-rounded integer-to-float conversion. """ + + # Constants, depending only on the floating-point format in use. + sig_bits = sys.float_info.mant_dig + 2 + q_max, shift_max = 1 << sig_bits, sys.float_info.max_exp - sig_bits + correction = [0, -1, -2, 1, 0, -1, 2, 1] + + # Reduce to case n positive. + if n == 0: + return 0.0 + elif n < 0: + return -long_to_float(-n) + + # q = top sig_bits significant bits of n, with 'sticky' lsb: that is, the + # lsb of the result is set iff it was set already, or any of the shifted + # out bits was set. + shift = n.bit_length() - sig_bits + q = n << -shift if shift < 0 else n >> shift | bool(n & (1 << shift) - 1) + + # Round half to even. + q += correction[q & 7] + + # Detect overflow. + if shift + (q == q_max) > shift_max: + raise OverflowError("integer too large to convert to float") + + # At this point: + # + # * The result of the correctly-rounded conversion is q * 2**shift, which + # is exactly representable, and within the range of a float. + # + # * q itself is an exactly representable integer in the range [q_max // + # 2, q_max]. q is a multiple of 4. + # + # Note that for the usual IEEE 754 format, q will fit into a 64-bit (signed + # or unsigned) integer at this point, so a low-level and efficient + # conversion of q to float(q) should be straightfoward to implement. + return math.ldexp(float(q), shift) + + + # Used for lazy formatting of failure messages class Frm(object): def __init__(self, format, *args): @@ -684,6 +726,9 @@ "test requires IEEE 754 doubles") def test_float_conversion(self): import sys + # Test 'long_to_float' instead of 'float' builtin. + float = long_to_float + DBL_MAX = sys.float_info.max DBL_MAX_EXP = sys.float_info.max_exp DBL_MANT_DIG = sys.float_info.mant_dig