Merge test_strtod and test_float string-to-float conversion tests.
This commit is contained in:
parent
c733b3252f
commit
801923681c
|
@ -8,7 +8,6 @@ import operator
|
||||||
import random
|
import random
|
||||||
import fractions
|
import fractions
|
||||||
import sys
|
import sys
|
||||||
import re
|
|
||||||
|
|
||||||
INF = float("inf")
|
INF = float("inf")
|
||||||
NAN = float("nan")
|
NAN = float("nan")
|
||||||
|
@ -22,74 +21,6 @@ requires_IEEE_754 = unittest.skipUnless(
|
||||||
test_dir = os.path.dirname(__file__) or os.curdir
|
test_dir = os.path.dirname(__file__) or os.curdir
|
||||||
format_testfile = os.path.join(test_dir, 'formatfloat_testcases.txt')
|
format_testfile = os.path.join(test_dir, 'formatfloat_testcases.txt')
|
||||||
|
|
||||||
finite_decimal_parser = re.compile(r""" # A numeric string consists of:
|
|
||||||
(?P<sign>[-+])? # an optional sign, followed by
|
|
||||||
(?=\d|\.\d) # a number with at least one digit
|
|
||||||
(?P<int>\d*) # having a (possibly empty) integer part
|
|
||||||
(?:\.(?P<frac>\d*))? # followed by an optional fractional part
|
|
||||||
(?:E(?P<exp>[-+]?\d+))? # and an optional exponent
|
|
||||||
\Z
|
|
||||||
""", re.VERBOSE | re.IGNORECASE | re.UNICODE).match
|
|
||||||
|
|
||||||
# Pure Python version of correctly rounded string->float conversion.
|
|
||||||
# Avoids any use of floating-point by returning the result as a hex string.
|
|
||||||
def strtod(s, mant_dig=53, min_exp = -1021, max_exp = 1024):
|
|
||||||
"""Convert a finite decimal string to a hex string representing an
|
|
||||||
IEEE 754 binary64 float. Return 'inf' or '-inf' on overflow.
|
|
||||||
This function makes no use of floating-point arithmetic at any
|
|
||||||
stage."""
|
|
||||||
|
|
||||||
# parse string into a pair of integers 'a' and 'b' such that
|
|
||||||
# abs(decimal value) = a/b, and a boolean 'negative'.
|
|
||||||
m = finite_decimal_parser(s)
|
|
||||||
if m is None:
|
|
||||||
raise ValueError('invalid numeric string')
|
|
||||||
fraction = m.group('frac') or ''
|
|
||||||
intpart = int(m.group('int') + fraction)
|
|
||||||
exp = int(m.group('exp') or '0') - len(fraction)
|
|
||||||
negative = m.group('sign') == '-'
|
|
||||||
a, b = intpart*10**max(exp, 0), 10**max(0, -exp)
|
|
||||||
|
|
||||||
# quick return for zeros
|
|
||||||
if not a:
|
|
||||||
return '-0x0.0p+0' if negative else '0x0.0p+0'
|
|
||||||
|
|
||||||
# compute exponent e for result; may be one too small in the case
|
|
||||||
# that the rounded value of a/b lies in a different binade from a/b
|
|
||||||
d = a.bit_length() - b.bit_length()
|
|
||||||
d += (a >> d if d >= 0 else a << -d) >= b
|
|
||||||
e = max(d, min_exp) - mant_dig
|
|
||||||
|
|
||||||
# approximate a/b by number of the form q * 2**e; adjust e if necessary
|
|
||||||
a, b = a << max(-e, 0), b << max(e, 0)
|
|
||||||
q, r = divmod(a, b)
|
|
||||||
if 2*r > b or 2*r == b and q & 1:
|
|
||||||
q += 1
|
|
||||||
if q.bit_length() == mant_dig+1:
|
|
||||||
q //= 2
|
|
||||||
e += 1
|
|
||||||
|
|
||||||
# double check that (q, e) has the right form
|
|
||||||
assert q.bit_length() <= mant_dig and e >= min_exp - mant_dig
|
|
||||||
assert q.bit_length() == mant_dig or e == min_exp - mant_dig
|
|
||||||
|
|
||||||
# check for overflow and underflow
|
|
||||||
if e + q.bit_length() > max_exp:
|
|
||||||
return '-inf' if negative else 'inf'
|
|
||||||
if not q:
|
|
||||||
return '-0x0.0p+0' if negative else '0x0.0p+0'
|
|
||||||
|
|
||||||
# for hex representation, shift so # bits after point is a multiple of 4
|
|
||||||
hexdigs = 1 + (mant_dig-2)//4
|
|
||||||
shift = 3 - (mant_dig-2)%4
|
|
||||||
q, e = q << shift, e - shift
|
|
||||||
return '{}0x{:x}.{:0{}x}p{:+d}'.format(
|
|
||||||
'-' if negative else '',
|
|
||||||
q // 16**hexdigs,
|
|
||||||
q % 16**hexdigs,
|
|
||||||
hexdigs,
|
|
||||||
e + 4*hexdigs)
|
|
||||||
|
|
||||||
class GeneralFloatCases(unittest.TestCase):
|
class GeneralFloatCases(unittest.TestCase):
|
||||||
|
|
||||||
def test_float(self):
|
def test_float(self):
|
||||||
|
@ -1378,38 +1309,6 @@ class HexFloatTestCase(unittest.TestCase):
|
||||||
else:
|
else:
|
||||||
self.identical(x, fromHex(toHex(x)))
|
self.identical(x, fromHex(toHex(x)))
|
||||||
|
|
||||||
class StrtodTestCase(unittest.TestCase):
|
|
||||||
def check_string(self, s):
|
|
||||||
expected = strtod(s)
|
|
||||||
try:
|
|
||||||
fs = float(s)
|
|
||||||
except OverflowError:
|
|
||||||
got = '-inf' if s[0] == '-' else 'inf'
|
|
||||||
else:
|
|
||||||
got = fs.hex()
|
|
||||||
self.assertEqual(expected, got,
|
|
||||||
"Incorrectly rounded str->float conversion for "
|
|
||||||
"{}: expected {}, got {}".format(s, expected, got))
|
|
||||||
|
|
||||||
@unittest.skipUnless(getattr(sys, 'float_repr_style', '') == 'short',
|
|
||||||
"applies only when using short float repr style")
|
|
||||||
def test_bug7632(self):
|
|
||||||
# check a few particular values that gave incorrectly rounded
|
|
||||||
# results with previous versions of dtoa.c
|
|
||||||
test_strings = [
|
|
||||||
'94393431193180696942841837085033647913224148539854e-358',
|
|
||||||
'12579816049008305546974391768996369464963024663104e-357',
|
|
||||||
'17489628565202117263145367596028389348922981857013e-357',
|
|
||||||
'18487398785991994634182916638542680759613590482273e-357',
|
|
||||||
'32002864200581033134358724675198044527469366773928e-358',
|
|
||||||
'73608278998966969345824653500136787876436005957953e-358',
|
|
||||||
'64774478836417299491718435234611299336288082136054e-358',
|
|
||||||
'13704940134126574534878641876947980878824688451169e-357',
|
|
||||||
'46697445774047060960624497964425416610480524760471e-358',
|
|
||||||
]
|
|
||||||
for s in test_strings:
|
|
||||||
self.check_string(s)
|
|
||||||
|
|
||||||
|
|
||||||
def test_main():
|
def test_main():
|
||||||
test_support.run_unittest(
|
test_support.run_unittest(
|
||||||
|
@ -1421,7 +1320,6 @@ def test_main():
|
||||||
RoundTestCase,
|
RoundTestCase,
|
||||||
InfNanTest,
|
InfNanTest,
|
||||||
HexFloatTestCase,
|
HexFloatTestCase,
|
||||||
StrtodTestCase,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -23,6 +23,8 @@ strtod_parser = re.compile(r""" # A numeric string consists of:
|
||||||
\Z
|
\Z
|
||||||
""", re.VERBOSE | re.IGNORECASE).match
|
""", re.VERBOSE | re.IGNORECASE).match
|
||||||
|
|
||||||
|
# Pure Python version of correctly rounded string->float conversion.
|
||||||
|
# Avoids any use of floating-point by returning the result as a hex string.
|
||||||
def strtod(s, mant_dig=53, min_exp = -1021, max_exp = 1024):
|
def strtod(s, mant_dig=53, min_exp = -1021, max_exp = 1024):
|
||||||
"""Convert a finite decimal string to a hex string representing an
|
"""Convert a finite decimal string to a hex string representing an
|
||||||
IEEE 754 binary64 float. Return 'inf' or '-inf' on overflow.
|
IEEE 754 binary64 float. Return 'inf' or '-inf' on overflow.
|
||||||
|
@ -259,6 +261,10 @@ class StrtodTests(unittest.TestCase):
|
||||||
'18487398785991994634182916638542680759613590482273e-357',
|
'18487398785991994634182916638542680759613590482273e-357',
|
||||||
'32002864200581033134358724675198044527469366773928e-358',
|
'32002864200581033134358724675198044527469366773928e-358',
|
||||||
'94393431193180696942841837085033647913224148539854e-358',
|
'94393431193180696942841837085033647913224148539854e-358',
|
||||||
|
'73608278998966969345824653500136787876436005957953e-358',
|
||||||
|
'64774478836417299491718435234611299336288082136054e-358',
|
||||||
|
'13704940134126574534878641876947980878824688451169e-357',
|
||||||
|
'46697445774047060960624497964425416610480524760471e-358',
|
||||||
# failing case for bug introduced by METD in r77451 (attempted
|
# failing case for bug introduced by METD in r77451 (attempted
|
||||||
# fix for issue 7632, bug 2), and fixed in r77482.
|
# fix for issue 7632, bug 2), and fixed in r77482.
|
||||||
'28639097178261763178489759107321392745108491825303e-311',
|
'28639097178261763178489759107321392745108491825303e-311',
|
||||||
|
|
Loading…
Reference in New Issue