[pypy-svn] r72705 - in pypy/branch/fix-64/pypy/objspace/std: . test

arigo at codespeak.net arigo at codespeak.net
Wed Mar 24 14:10:42 CET 2010


Author: arigo
Date: Wed Mar 24 14:10:40 2010
New Revision: 72705

Modified:
   pypy/branch/fix-64/pypy/objspace/std/floatobject.py
   pypy/branch/fix-64/pypy/objspace/std/test/test_floatobject.py
Log:
Fix for code like "62-bit-int == float", on 64-bit platforms
where casting the int to a float might loose precision.
Done with more verbose but more regular code in floatobject.
Drawback: at app-level, "5 .__eq__(3.4)" now returns False
instead of NotImplemented.  Do we care at all?


Modified: pypy/branch/fix-64/pypy/objspace/std/floatobject.py
==============================================================================
--- pypy/branch/fix-64/pypy/objspace/std/floatobject.py	(original)
+++ pypy/branch/fix-64/pypy/objspace/std/floatobject.py	Wed Mar 24 14:10:40 2010
@@ -1,9 +1,11 @@
+import operator, new
 from pypy.objspace.std.objspace import *
 from pypy.interpreter import gateway
 from pypy.objspace.std.noneobject import W_NoneObject
 from pypy.objspace.std.longobject import W_LongObject
 from pypy.rlib.rarithmetic import ovfcheck_float_to_int, intmask, isinf, isnan
-from pypy.rlib.rarithmetic import formatd
+from pypy.rlib.rarithmetic import formatd, LONG_BIT
+from pypy.rlib.rbigint import rbigint
 
 import math
 from pypy.objspace.std.intobject import W_IntObject
@@ -88,82 +90,115 @@
     s = formatd("%.12g", x)
     return space.wrap(should_not_look_like_an_int(s))
 
+# ____________________________________________________________
+# A mess to handle all cases of float comparison without relying
+# on delegation, which can unfortunately loose precision when
+# casting an int or a long to a float.
+
+def list_compare_funcs(declarator):
+    for op in ['lt', 'le', 'eq', 'ne', 'gt', 'ge']:
+        func, name = declarator(op)
+        globals()[name] = func_with_new_name(func, name)
+
+def _reverse(opname):
+    if opname[0] == 'l': return 'g' + opname[1:]
+    elif opname[0] == 'g': return 'l' + opname[1:]
+    else: return opname
 
-def declare_new_float_comparison(opname):
-    import operator
-    from pypy.tool.sourcetools import func_with_new_name
-    op = getattr(operator, opname)
-    def f(space, w_int1, w_int2):
-        i = w_int1.floatval
-        j = w_int2.floatval
-        return space.newbool(op(i, j))
-    name = opname + "__Float_Float"
-    return func_with_new_name(f, name), name
-
-for op in ['lt', 'le', 'eq', 'ne', 'gt', 'ge']:
-    func, name = declare_new_float_comparison(op)
-    globals()[name] = func
-
-# for overflowing comparisons between longs and floats
-# XXX we might have to worry (later) about eq__Float_Int, for the case
-#     where int->float conversion may lose precision :-(
-def eq__Float_Long(space, w_float1, w_long2):
-    # XXX naive implementation
-    x = w_float1.floatval
-    if isinf(x) or math.floor(x) != x:
-        return space.w_False
-    try:
-        w_long1 = W_LongObject.fromfloat(x)
-    except OverflowError:
-        return space.w_False
-    return space.eq(w_long1, w_long2)
-
-def eq__Long_Float(space, w_long1, w_float2):
-    return eq__Float_Long(space, w_float2, w_long1)
-
-def ne__Float_Long(space, w_float1, w_long2):
-    return space.not_(eq__Float_Long(space, w_float1, w_long2))
-
-def ne__Long_Float(space, w_long1, w_float2):
-    return space.not_(eq__Float_Long(space, w_float2, w_long1))
-
-def lt__Float_Long(space, w_float1, w_long2):
-    # XXX naive implementation
-    x = w_float1.floatval
-    if isinf(x):
-        return space.newbool(x < 0.0)
-    x_floor = math.floor(x)
-    try:
-        w_long1 = W_LongObject.fromfloat(x_floor)
-    except OverflowError:
-        return space.newbool(x < 0.0)
-    return space.lt(w_long1, w_long2)
-
-def lt__Long_Float(space, w_long1, w_float2):
-    return space.not_(le__Float_Long(space, w_float2, w_long1))
 
-def le__Float_Long(space, w_float1, w_long2):
-    # XXX it's naive anyway
-    if space.is_true(space.lt(w_float1, w_long2)):
-        return space.w_True
+def declare_compare_bigint(opname):
+    """Return a helper function that implements a float-bigint comparison."""
+    op = getattr(operator, opname)
+    #
+    if opname == 'eq' or opname == 'ne':
+        def do_compare_bigint(f1, b2):
+            """f1 is a float.  b2 is a bigint."""
+            if isinf(f1) or isnan(f1) or math.floor(f1) != f1:
+                return opname == 'ne'
+            b1 = rbigint.fromfloat(f1)
+            res = b1.eq(b2)
+            if opname == 'ne':
+                res = not res
+            return res
     else:
-        return space.eq(w_float1, w_long2)
+        def do_compare_bigint(f1, b2):
+            """f1 is a float.  b2 is a bigint."""
+            if isinf(f1) or isnan(f1):
+                return op(f1, 0.0)
+            if opname == 'gt' or opname == 'le':
+                # 'float > long'   <==>  'ceil(float) > long'
+                # 'float <= long'  <==>  'ceil(float) <= long'
+                f1 = math.ceil(f1)
+            else:
+                # 'float < long'   <==>  'floor(float) < long'
+                # 'float >= long'  <==>  'floor(float) >= long'
+                f1 = math.floor(f1)
+            b1 = rbigint.fromfloat(f1)
+            return getattr(b1, opname)(b2)
+    #
+    return do_compare_bigint, 'compare_bigint_' + opname
+list_compare_funcs(declare_compare_bigint)
 
-def le__Long_Float(space, w_long1, w_float2):
-    return space.not_(lt__Float_Long(space, w_float2, w_long1))
 
-def gt__Float_Long(space, w_float1, w_long2):
-    return space.not_(le__Float_Long(space, w_float1, w_long2))
+def declare_cmp_float_float(opname):
+    op = getattr(operator, opname)
+    def f(space, w_float1, w_float2):
+        f1 = w_float1.floatval
+        f2 = w_float2.floatval
+        return space.newbool(op(f1, f2))
+    return f, opname + "__Float_Float"
+list_compare_funcs(declare_cmp_float_float)
 
-def gt__Long_Float(space, w_long1, w_float2):
-    return lt__Float_Long(space, w_float2, w_long1)
+def declare_cmp_float_int(opname):
+    op = getattr(operator, opname)
+    compare = globals()['compare_bigint_' + opname]
+    def f(space, w_float1, w_int2):
+        f1 = w_float1.floatval
+        i2 = w_int2.intval
+        f2 = float(i2)
+        if LONG_BIT > 32 and int(f2) != i2:
+            res = compare(f1, rbigint.fromint(i2))
+        else:
+            res = op(f1, f2)
+        return space.newbool(res)
+    return f, opname + "__Float_Int"
+list_compare_funcs(declare_cmp_float_int)
+
+def declare_cmp_float_long(opname):
+    compare = globals()['compare_bigint_' + opname]
+    def f(space, w_float1, w_long2):
+        f1 = w_float1.floatval
+        b2 = w_long2.num
+        return space.newbool(compare(f1, b2))
+    return f, opname + "__Float_Long"
+list_compare_funcs(declare_cmp_float_long)
 
-def ge__Float_Long(space, w_float1, w_long2):
-    return space.not_(lt__Float_Long(space, w_float1, w_long2))
+def declare_cmp_int_float(opname):
+    op = getattr(operator, opname)
+    revcompare = globals()['compare_bigint_' + _reverse(opname)]
+    def f(space, w_int1, w_float2):
+        f2 = w_float2.floatval
+        i1 = w_int1.intval
+        f1 = float(i1)
+        if LONG_BIT > 32 and int(f1) != i1:
+            res = revcompare(f2, rbigint.fromint(i1))
+        else:
+            res = op(f1, f2)
+        return space.newbool(res)
+    return f, opname + "__Int_Float"
+list_compare_funcs(declare_cmp_int_float)
+
+def declare_cmp_long_float(opname):
+    revcompare = globals()['compare_bigint_' + _reverse(opname)]
+    def f(space, w_long1, w_float2):
+        f2 = w_float2.floatval
+        b1 = w_long1.num
+        return space.newbool(revcompare(f2, b1))
+    return f, opname + "__Long_Float"
+list_compare_funcs(declare_cmp_long_float)
 
-def ge__Long_Float(space, w_long1, w_float2):
-    return le__Float_Long(space, w_float2, w_long1)
 
+# ____________________________________________________________
 
 def hash__Float(space, w_value):
     return space.wrap(_hash_float(space, w_value.floatval))

Modified: pypy/branch/fix-64/pypy/objspace/std/test/test_floatobject.py
==============================================================================
--- pypy/branch/fix-64/pypy/objspace/std/test/test_floatobject.py	(original)
+++ pypy/branch/fix-64/pypy/objspace/std/test/test_floatobject.py	Wed Mar 24 14:10:40 2010
@@ -212,6 +212,8 @@
         assert 13 <= 13.01
 
     def test_comparison_more(self):
+        import sys
+        is_pypy = '__pypy__' in sys.builtin_module_names
         infinity = 1e200*1e200
         nan = infinity/infinity
         for x in (123, 1 << 30,
@@ -239,12 +241,27 @@
             assert     ((x + 1) >  float(x))
             assert not ((x + 1) <  float(x))
             #
-            #assert not (x == nan)
-            #assert not (x >= nan)
-            #assert not (x <= nan)
-            #assert     (x != nan)
-            #assert not (x >  nan)
-            #assert not (x <  nan)
+            assert not (x == infinity)
+            assert not (x >= infinity)
+            assert     (x <= infinity)
+            assert     (x != infinity)
+            assert not (x >  infinity)
+            assert     (x <  infinity)
+            #
+            assert not (x == -infinity)
+            assert     (x >= -infinity)
+            assert not (x <= -infinity)
+            assert     (x != -infinity)
+            assert     (x >  -infinity)
+            assert not (x <  -infinity)
+            #
+            if is_pypy:
+                assert not (x == nan)
+                assert not (x >= nan)
+                assert not (x <= nan)
+                assert     (x != nan)
+                assert not (x >  nan)
+                assert not (x <  nan)
             #
             assert     (float(x) == x)
             assert     (float(x) <= x)
@@ -267,19 +284,35 @@
             assert     (float(x) <  (x + 1))
             assert not (float(x) >  (x + 1))
             #
-            #assert not (nan == x)
-            #assert not (nan <= x)
-            #assert not (nan >= x)
-            #assert     (nan != x)
-            #assert not (nan <  x)
-            #assert not (nan >  x)
+            assert not (infinity == x)
+            assert     (infinity >= x)
+            assert not (infinity <= x)
+            assert     (infinity != x)
+            assert     (infinity >  x)
+            assert not (infinity <  x)
+            #
+            assert not (-infinity == x)
+            assert not (-infinity >= x)
+            assert     (-infinity <= x)
+            assert     (-infinity != x)
+            assert not (-infinity >  x)
+            assert     (-infinity <  x)
+            #
+            if is_pypy:
+                assert not (nan == x)
+                assert not (nan <= x)
+                assert not (nan >= x)
+                assert     (nan != x)
+                assert not (nan <  x)
+                assert not (nan >  x)
 
     def test_multimethod_slice(self):
         assert 5 .__add__(3.14) is NotImplemented
         assert 3.25 .__add__(5) == 8.25
-        if hasattr(int, '__eq__'):  # for py.test -A: CPython is inconsistent
-            assert 5 .__eq__(3.14) is NotImplemented
-            assert 3.14 .__eq__(5) is False
+        # xxx we are also a bit inconsistent about the following
+        #if hasattr(int, '__eq__'):  # for py.test -A: CPython is inconsistent
+        #    assert 5 .__eq__(3.14) is NotImplemented
+        #    assert 3.14 .__eq__(5) is False
         #if hasattr(long, '__eq__'):  # for py.test -A: CPython is inconsistent
         #    assert 5L .__eq__(3.14) is NotImplemented
         #    assert 3.14 .__eq__(5L) is False



More information about the Pypy-commit mailing list