[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