[pypy-svn] r74793 - in pypy/trunk/pypy/objspace/std: . test

fijal at codespeak.net fijal at codespeak.net
Wed May 26 21:31:17 CEST 2010


Author: fijal
Date: Wed May 26 21:31:15 2010
New Revision: 74793

Modified:
   pypy/trunk/pypy/objspace/std/floattype.py
   pypy/trunk/pypy/objspace/std/strutil.py
   pypy/trunk/pypy/objspace/std/test/test_floatobject.py
   pypy/trunk/pypy/objspace/std/test/test_strutil.py
Log:
* Fix issue 534 by special casing -inf inf nan and -nan (not sure if that's
  enough though) when doing float(string)
* Remove unused code (the comment said "for reference", but I don't see a point)
* Run tests over code that we use and not test unused functions


Modified: pypy/trunk/pypy/objspace/std/floattype.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/floattype.py	(original)
+++ pypy/trunk/pypy/objspace/std/floattype.py	Wed May 26 21:31:15 2010
@@ -1,21 +1,16 @@
 from pypy.interpreter import gateway
 from pypy.interpreter.error import OperationError
 from pypy.objspace.std.stdtypedef import StdTypeDef
-from pypy.objspace.std.strutil import string_to_float, ParseStringError
+from pypy.objspace.std.strutil import ParseStringError
 from pypy.objspace.std.strutil import interp_string_to_float
 
-USE_NEW_S2F = True
-
 def descr__new__(space, w_floattype, w_x=0.0):
     from pypy.objspace.std.floatobject import W_FloatObject
     w_value = w_x     # 'x' is the keyword argument name in CPython
     if space.is_true(space.isinstance(w_value, space.w_str)):
         strvalue = space.str_w(w_value)
         try:
-            if USE_NEW_S2F:
-                value = interp_string_to_float(space, strvalue)
-            else:
-                value = string_to_float(strvalue)
+            value = interp_string_to_float(space, strvalue)
         except ParseStringError, e:
             raise OperationError(space.w_ValueError,
                                  space.wrap(e.msg))
@@ -26,10 +21,7 @@
             from unicodeobject import unicode_to_decimal_w
         strvalue = unicode_to_decimal_w(space, w_value)
         try:
-            if USE_NEW_S2F:
-                value = interp_string_to_float(space, strvalue)
-            else:
-                value = string_to_float(strvalue)
+            value = interp_string_to_float(space, strvalue)
         except ParseStringError, e:
             raise OperationError(space.w_ValueError,
                                  space.wrap(e.msg))

Modified: pypy/trunk/pypy/objspace/std/strutil.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/strutil.py	(original)
+++ pypy/trunk/pypy/objspace/std/strutil.py	Wed May 26 21:31:15 2010
@@ -2,7 +2,8 @@
 Pure Python implementation of string utilities.
 """
 
-from pypy.rlib.rarithmetic import ovfcheck, break_up_float, parts_to_float
+from pypy.rlib.rarithmetic import ovfcheck, break_up_float, parts_to_float,\
+     INFINITY, NAN
 from pypy.interpreter.error import OperationError
 import math
 
@@ -138,33 +139,6 @@
             return w_result
         w_result = space.add(space.mul(w_result,w_base), space.newlong(digit))
 
-def string_to_float(s):
-    """
-    Conversion of string to float.
-    This version tries to only raise on invalid literals.
-    Overflows should be converted to infinity whenever possible.
-    """
-
-    s = strip_spaces(s)
-
-    if not s:
-        raise ParseStringError("empty string for float()")
-
-    # 1) parse the string into pieces.
-    try:
-        sign, before_point, after_point, exponent = break_up_float(s)
-    except ValueError:
-        raise ParseStringError("invalid literal for float()")
-    
-    if not before_point and not after_point:
-        raise ParseStringError("invalid literal for float()")
-
-    try:
-        return parts_to_float(sign, before_point, after_point, exponent)
-    except ValueError:
-        raise ParseStringError("invalid literal for float()")
-
-
 # Tim's comment:
 # 57 bits are more than needed in any case.
 # to allow for some rounding, we take one
@@ -187,140 +161,6 @@
 del calc_mantissa_bits
 MANTISSA_DIGITS = len(str( (1L << MANTISSA_BITS)-1 )) + 1
 
-# we keep this version for reference.
-def applevel_string_to_float(s):
-    """
-    Conversion of string to float.
-    This version tries to only raise on invalid literals.
-    Overflows should be converted to infinity whenever possible.
-    """
-    # this version was triggered by Python 2.4 which adds
-    # a test that breaks on overflow.
-    # XXX The test still breaks for a different reason:
-    # float must implement rich comparisons, where comparison
-    # between infinity and a too large long does not overflow!
-
-    # The problem:
-    # there can be extreme notations of floats which are not
-    # infinity.
-    # For instance, this works in CPython:
-    # float('1' + '0'*1000 + 'e-1000')
-    # should evaluate to 1.0.
-    # note: float('1' + '0'*10000 + 'e-10000')
-    # does not work in CPython, but PyPy can do it, now.
-
-    # The idea:
-    # in order to compensate between very long digit strings
-    # and extreme exponent numbers, we try to avoid overflows
-    # by adjusting the exponent by the number of mantissa
-    # digits. For simplicity, all computations are done in
-    # long math.
-
-    # The plan:
-    # 1) parse the string into pieces.
-    # 2) pre-calculate digit exponent dexp.
-    # 3) truncate and adjust dexp.
-    # 4) compute the exponent and truncate to +-400.
-    # 5) compute the value using long math and proper rounding.
-
-    # Positive results:
-    # The algorithm appears appears to produce correct round-trip
-    # values for the perfect input of _float_formatting.
-    # Note:
-    # XXX: the builtin rounding of long->float does not work, correctly.
-    # Ask Tim Peters for the reasons why no correct rounding is done.
-    # XXX: limitations:
-    # - It is possibly not too efficient.
-    # - Really optimum results need a more sophisticated algorithm
-    #   like Bellerophon from William D. Clinger, cf.
-    #   http://citeseer.csail.mit.edu/clinger90how.html
-    
-    s = strip_spaces(s)
-
-    if not s:
-        raise ParseStringError("empty string for float()")
-
-    # 1) parse the string into pieces.
-    try:
-        sign, before_point, after_point, exponent = break_up_float(s)
-    except ValueError:
-        raise ParseStringError("invalid literal for float()")
-        
-    digits = before_point + after_point
-    if digits:
-        raise ParseStringError("invalid literal for float()")
-
-    # 2) pre-calculate digit exponent dexp.
-    dexp = len(before_point)
-
-    # 3) truncate and adjust dexp.
-    p = 0
-    plim = dexp + len(after_point)
-    while p < plim and digits[p] == '0':
-        p += 1
-        dexp -= 1
-    digits = digits[p : p + MANTISSA_DIGITS]
-    p = len(digits) - 1
-    while p >= 0 and digits[p] == '0':
-        p -= 1
-    dexp -= p + 1
-    digits = digits[:p+1]
-    if len(digits) == 0:
-        digits = '0'
-
-    # 4) compute the exponent and truncate to +-400
-    if not exponent:
-        exponent = '0'
-    e = long(exponent) + dexp
-    if e >= 400:
-        e = 400
-    elif e <= -400:
-        e = -400
-
-    # 5) compute the value using long math and proper rounding.
-    lr = long(digits)
-    if e >= 0:
-        bits = 0
-        m = lr * 10L ** e
-    else:
-        # compute a sufficiently large scale
-        prec = MANTISSA_DIGITS * 2 + 22 # 128, maybe
-        bits = - (int(math.ceil(-e / math.log10(2.0) - 1e-10)) + prec)
-        scale = 2L ** -bits
-        pten = 10L ** -e
-        m = (lr * scale) // pten
-
-    # we now have a fairly large mantissa.
-    # Shift it and round the last bit.
-
-    # first estimate the bits and do a big shift
-    if m:
-        mbits = int(math.ceil(math.log(m, 2) - 1e-10))
-        needed = MANTISSA_BITS
-        if mbits > needed:
-            if mbits > needed+1:
-                shifted = mbits - (needed+1)
-                m >>= shifted
-                bits += shifted
-            # do the rounding
-            bits += 1
-            m = (m >> 1) + (m & 1)
-
-    try:
-        r = math.ldexp(m, bits)
-    except OverflowError:
-        r = 1e200 * 1e200 # produce inf, hopefully
-
-    if sign == '-':
-        r = -r
-
-    return r
-
-
-# the "real" implementation.
-# for comments, see above.
-# XXX probably this very specific thing should go into longobject?
-
 def interp_string_to_float(space, s):
     """
     Conversion of string to float.
@@ -336,6 +176,15 @@
         raise OperationError(space.w_ValueError, space.wrap(
             "empty string for float()"))
 
+    
+    low = s.lower()
+    if low == "-inf":
+        return -INFINITY
+    elif low == "inf":
+        return INFINITY
+    elif low == "nan" or low == "-nan":
+        return NAN
+
     # 1) parse the string into pieces.
     try:
         sign, before_point, after_point, exponent = break_up_float(s)
@@ -432,7 +281,7 @@
         if r == 2*r and r != 0.0:
             raise OverflowError
     except OverflowError:
-        r = 1e200 * 1e200 # produce inf, hopefully
+        r = INFINITY
 
     if sign == '-':
         r = -r

Modified: pypy/trunk/pypy/objspace/std/test/test_floatobject.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/test/test_floatobject.py	(original)
+++ pypy/trunk/pypy/objspace/std/test/test_floatobject.py	Wed May 26 21:31:15 2010
@@ -92,6 +92,9 @@
     def test_float_string(self):
         assert 42 == float("42")
         assert 42.25 == float("42.25")
+        assert str(float("inf")).startswith("inf")
+        assert str(float("-INf")).startswith("-inf")
+        assert str(float("-nAn")).startswith("nan")
 
     def test_float_unicode(self):
         # u00A0 and u2000 are some kind of spaces

Modified: pypy/trunk/pypy/objspace/std/test/test_strutil.py
==============================================================================
--- pypy/trunk/pypy/objspace/std/test/test_strutil.py	(original)
+++ pypy/trunk/pypy/objspace/std/test/test_strutil.py	Wed May 26 21:31:15 2010
@@ -1,5 +1,6 @@
 import py, random
 from pypy.objspace.std.strutil import *
+from pypy.interpreter.error import OperationError
 
 import py
 
@@ -132,50 +133,53 @@
         assert string_to_w_long(space, '123L', 21).longval() == 441 + 42 + 3
         assert string_to_w_long(space, '1891234174197319').longval() == 1891234174197319
 
-def test_string_to_float():
-    assert string_to_float('0') == 0.0
-    assert string_to_float('1') == 1.0
-    assert string_to_float('-1.5') == -1.5
-    assert string_to_float('1.5E2') == 150.0
-    assert string_to_float('2.5E-1') == 0.25
-    assert string_to_float('1e1111111111111') == float('1e1111111111111')
-    assert string_to_float('1e-1111111111111') == float('1e-1111111111111')
-    assert string_to_float('-1e1111111111111') == float('-1e1111111111111')
-    assert string_to_float('-1e-1111111111111') == float('-1e-1111111111111')
-    assert string_to_float('1e111111111111111111111') == float('1e111111111111111111111')
-    assert string_to_float('1e-111111111111111111111') == float('1e-111111111111111111111')
-    assert string_to_float('-1e111111111111111111111') == float('-1e111111111111111111111')
-    assert string_to_float('-1e-111111111111111111111') == float('-1e-111111111111111111111')
-
-    valid_parts = [['', '  ', ' \f\n\r\t\v'],
-                   ['', '+', '-'],
-                   ['00', '90', '.5', '2.4', '3.', '0.07',
-                    '12.3489749871982471987198371293717398256187563298638726'
-                    '2187362820947193247129871083561249818451804287437824015'
-                    '013816418758104762348932657836583048761487632840726386'],
-                   ['', 'e0', 'E+1', 'E-01', 'E42'],
-                   ['', '  ', ' \f\n\r\t\v'],
-                   ]
-    invalid_parts = [['#'],
-                     ['++', '+-', '-+', '--'],
-                     ['', '1.2.3', '.', '5..6'],
-                     ['E+', 'E-', 'e', 'e++', 'E++2'],
-                     ['#'],
-                     ]
-    for part0 in valid_parts[0]:
-        for part1 in valid_parts[1]:
-            for part2 in valid_parts[2]:
-                for part3 in valid_parts[3]:
-                    for part4 in valid_parts[4]:
-                        s = part0+part1+part2+part3+part4
-                        assert (abs(string_to_float(s) - float(s)) <=
-                                1E-13 * abs(float(s)))
-
-    for j in range(len(invalid_parts)):
-        for invalid in invalid_parts[j]:
-            for i in range(20):
-                parts = [random.choice(lst) for lst in valid_parts]
-                parts[j] = invalid
-                s = ''.join(parts)
-                print repr(s)
-                py.test.raises(ParseStringError, string_to_float, s)
+    def test_string_to_float(self):
+        def string_to_float(x):
+            return interp_string_to_float(self.space, x)
+        assert string_to_float('0') == 0.0
+        assert string_to_float('1') == 1.0
+        assert string_to_float('-1.5') == -1.5
+        assert string_to_float('1.5E2') == 150.0
+        assert string_to_float('2.5E-1') == 0.25
+        assert string_to_float('1e1111111111111') == float('1e1111111111111')
+        assert string_to_float('1e-1111111111111') == float('1e-1111111111111')
+        assert string_to_float('-1e1111111111111') == float('-1e1111111111111')
+        assert string_to_float('-1e-1111111111111') == float('-1e-1111111111111')
+        assert string_to_float('1e111111111111111111111') == float('1e111111111111111111111')
+        assert string_to_float('1e-111111111111111111111') == float('1e-111111111111111111111')
+        assert string_to_float('-1e111111111111111111111') == float('-1e111111111111111111111')
+        assert string_to_float('-1e-111111111111111111111') == float('-1e-111111111111111111111')
+
+        valid_parts = [['', '  ', ' \f\n\r\t\v'],
+                       ['', '+', '-'],
+                       ['00', '90', '.5', '2.4', '3.', '0.07',
+                        '12.3489749871982471987198371293717398256187563298638726'
+                        '2187362820947193247129871083561249818451804287437824015'
+                        '013816418758104762348932657836583048761487632840726386'],
+                       ['', 'e0', 'E+1', 'E-01', 'E42'],
+                       ['', '  ', ' \f\n\r\t\v'],
+                       ]
+        invalid_parts = [['#'],
+                         ['++', '+-', '-+', '--'],
+                         ['', '1.2.3', '.', '5..6'],
+                         ['E+', 'E-', 'e', 'e++', 'E++2'],
+                         ['#'],
+                         ]
+        for part0 in valid_parts[0]:
+            for part1 in valid_parts[1]:
+                for part2 in valid_parts[2]:
+                    for part3 in valid_parts[3]:
+                        for part4 in valid_parts[4]:
+                            s = part0+part1+part2+part3+part4
+                            assert (abs(string_to_float(s) - float(s)) <=
+                                    1E-13 * abs(float(s)))
+
+        for j in range(len(invalid_parts)):
+            for invalid in invalid_parts[j]:
+                for i in range(20):
+                    parts = [random.choice(lst) for lst in valid_parts]
+                    parts[j] = invalid
+                    s = ''.join(parts)
+                    print repr(s)
+                    if s.strip(): # empty s raises OperationError directly
+                        py.test.raises(ParseStringError, string_to_float, s)



More information about the Pypy-commit mailing list