Author: Amaury Forgeot d'Arc <[email protected]>
Branch: decimal-libmpdec
Changeset: r73595:b252f18bbe7e
Date: 2014-09-16 16:01 +0200
http://bitbucket.org/pypy/pypy/changeset/b252f18bbe7e/
Log: Implement comparisons with Rational and Fractions.
diff --git a/pypy/module/_decimal/__init__.py b/pypy/module/_decimal/__init__.py
--- a/pypy/module/_decimal/__init__.py
+++ b/pypy/module/_decimal/__init__.py
@@ -30,7 +30,5 @@
for name, flag in interp_signals.SIGNAL_MAP:
interpleveldefs[name] = 'interp_signals.get(space).w_%s' % name
for name, flag in interp_signals.COND_MAP:
- if name == 'InvalidOperation':
- pass
interpleveldefs[name] = 'interp_signals.get(space).w_%s' % name
diff --git a/pypy/module/_decimal/interp_decimal.py
b/pypy/module/_decimal/interp_decimal.py
--- a/pypy/module/_decimal/interp_decimal.py
+++ b/pypy/module/_decimal/interp_decimal.py
@@ -30,6 +30,15 @@
# DEC_MINALLOC >= MPD_MINALLOC
DEC_MINALLOC = 4
+class State:
+ def __init__(self, space):
+ w_import = space.builtin.get('__import__')
+ w_numbers = space.call_function(w_import,
+ space.wrap('numbers'))
+ self.w_Rational = space.getattr(w_numbers,
+ space.wrap('Rational'))
+
+
class W_Decimal(W_Root):
hash = -1
@@ -483,6 +492,46 @@
space.type(w_y))
return w_a, w_b
+# Convert rationals for comparison
+def _multiply_by_denominator(space, w_v, w_r, context):
+ # w_v is not special, w_r is a rational.
+ w_denom = space.getattr(w_r, space.wrap("denominator"))
+ denom = decimal_from_bigint(space, None, space.bigint_w(w_denom),
+ context, exact=True)
+ vv = rmpdec.mpd_qncopy(w_v.mpd)
+ if not vv:
+ raise OperationError(space.w_MemoryError, space.w_None)
+ try:
+ result = W_Decimal(space)
+ with lltype.scoped_alloc(rmpdec.MPD_CONTEXT_PTR.TO) as maxctx:
+ rmpdec.mpd_maxcontext(maxctx)
+ # Prevent Overflow in the following multiplication. The
+ # result of the multiplication is only used in mpd_qcmp,
+ # which can handle values that are technically out of
+ # bounds, like (for 32-bit)
+ # 99999999999999999999...99999999e+425000000.
+ exp = vv.c_exp
+ vv.c_exp = 0
+ with lltype.scoped_alloc(rffi.CArrayPtr(rffi.UINT).TO, 1,
+ zero=True) as status_ptr:
+ rmpdec.mpd_qmul(result.mpd, vv, denom.mpd, maxctx, status_ptr)
+ # If any status has been accumulated during the multiplication,
+ # the result is invalid. This is very unlikely, since even the
+ # 32-bit version supports 425000000 digits.
+ if status_ptr[0]:
+ raise oefmt(space.w_ValueError,
+ "exact conversion for comparison failed")
+ result.mpd.c_exp = exp
+ finally:
+ rmpdec.mpd_del(vv)
+
+ return space.wrap(result)
+
+def _numerator_as_decimal(space, w_r, context):
+ w_numerator = space.getattr(w_r, space.wrap("numerator"))
+ return decimal_from_bigint(space, None, space.bigint_w(w_numerator),
+ context, exact=True)
+
def convert_binop_cmp(space, context, op, w_v, w_w):
if isinstance(w_w, W_Decimal):
return None, w_v, w_w
@@ -513,6 +562,11 @@
w_w = decimal_from_float(space, None, w_w, context, exact=True)
else:
return space.w_NotImplemented, None, None
+ elif space.isinstance_w(w_w, space.fromcache(State).w_Rational):
+ w_numerator = _numerator_as_decimal(space, w_w, context)
+ if not rmpdec.mpd_isspecial(w_v.mpd):
+ w_v = _multiply_by_denominator(space, w_v, w_w, context)
+ w_w = w_numerator
else:
return space.w_NotImplemented, None, None
return None, w_v, w_w
diff --git a/pypy/module/_decimal/test/test_decimal.py
b/pypy/module/_decimal/test/test_decimal.py
--- a/pypy/module/_decimal/test/test_decimal.py
+++ b/pypy/module/_decimal/test/test_decimal.py
@@ -855,6 +855,49 @@
doit(c, signal=FloatOperation)
test_containers(c, signal=FloatOperation)
+ def test_decimal_fraction_comparison(self):
+ C = self.decimal
+ D = self.decimal.Decimal
+ from fractions import Fraction as F
+ Context = self.decimal.Context
+ localcontext = self.decimal.localcontext
+ InvalidOperation = self.decimal.InvalidOperation
+
+
+ emax = C.MAX_EMAX
+ emin = C.MIN_EMIN
+ etiny = C.MIN_ETINY
+ c = Context(Emax=emax, Emin=emin)
+
+ with localcontext(c):
+ c.prec = emax
+ assert D(0) < F(1,9999999999999999999999999999999999999)
+ assert F(-1,9999999999999999999999999999999999999) < D(0)
+ assert F(0,1) < D("1e" + str(etiny))
+ assert D("-1e" + str(etiny)) < F(0,1)
+ assert F(0,9999999999999999999999999) < D("1e" + str(etiny))
+ assert D("-1e" + str(etiny)) < F(0,9999999999999999999999999)
+
+ assert D("0.1") == F(1,10)
+ assert F(1,10) == D("0.1")
+
+ c.prec = 300
+ assert D(1)/3 != F(1,3)
+ assert F(1,3) != D(1)/3
+
+ assert F(120984237, 9999999999) <= D("9e" + str(emax))
+ assert D("9e" + str(emax)) >= F(120984237, 9999999999)
+
+ assert D('inf') > F(99999999999,123)
+ assert D('inf') > F(-99999999999,123)
+ assert D('-inf') < F(99999999999,123)
+ assert D('-inf') < F(-99999999999,123)
+
+ raises(InvalidOperation, D('nan').__gt__, F(-9,123))
+ assert F(-9,123).__lt__(D('nan')) is NotImplemented
+ assert D('nan') != F(-9,123)
+ assert F(-9,123) != D('nan')
+
def test_nan_comparisons(self):
import operator
# comparisons involving signaling nans signal InvalidOperation
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit