Author: Amaury Forgeot d'Arc <[email protected]>
Branch: decimal-libmpdec
Changeset: r73596:c48bbeeb5798
Date: 2014-09-17 22:06 +0200
http://bitbucket.org/pypy/pypy/changeset/c48bbeeb5798/
Log: One more test, many methods to add.
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
@@ -360,6 +360,26 @@
def descr_rpow(self, space, w_other):
return W_Decimal.pow_impl(space, w_other, self, None)
+ def copy_abs_w(self, space):
+ w_result = W_Decimal.allocate(space)
+ with lltype.scoped_alloc(rffi.CArrayPtr(rffi.UINT).TO, 1,
+ zero=True) as status_ptr:
+ rmpdec.mpd_qcopy_abs(w_result.mpd, self.mpd, status_ptr)
+ status = rffi.cast(lltype.Signed, status_ptr[0])
+ if status & rmpdec.MPD_Malloc_error:
+ raise OperationError(space.w_MemoryError, space.w_None)
+ return w_result
+
+ def copy_negate_w(self, space):
+ w_result = W_Decimal.allocate(space)
+ with lltype.scoped_alloc(rffi.CArrayPtr(rffi.UINT).TO, 1,
+ zero=True) as status_ptr:
+ rmpdec.mpd_qcopy_negate(w_result.mpd, self.mpd, status_ptr)
+ status = rffi.cast(lltype.Signed, status_ptr[0])
+ if status & rmpdec.MPD_Malloc_error:
+ raise OperationError(space.w_MemoryError, space.w_None)
+ return w_result
+
def copy_sign_w(self, space, w_other, w_context=None):
context = convert_context(space, w_context)
w_other = convert_op_raise(space, context, w_other)
@@ -371,6 +391,11 @@
# Unary arithmetic functions, optional context arg
+ def number_class_w(self, space, w_context=None):
+ context = interp_context.ensure_context(space, w_context)
+ cp = rmpdec.mpd_class(self.mpd, context.ctx)
+ return space.wrap(rffi.charp2str(cp))
+
def to_integral_w(self, space, w_rounding=None, w_context=None):
context = interp_context.ensure_context(space, w_context)
w_workctx = context.copy_w(space)
@@ -396,13 +421,42 @@
rmpdec.mpd_qround_to_intx(w_result.mpd, self.mpd,
w_workctx.ctx, status_ptr)
return w_result
+
+ # Ternary arithmetic functions, optional context arg
+ def fma_w(self, space, w_other, w_third, w_context=None):
+ context = interp_context.ensure_context(space, w_context)
+ w_a, w_b, w_c = convert_ternop_raise(space, context,
+ self, w_other, w_third)
+ w_result = W_Decimal.allocate(space)
+ with context.catch_status(space) as (ctx, status_ptr):
+ rmpdec.mpd_qfma(w_result.mpd, w_a.mpd, w_b.mpd, w_c.mpd,
+ ctx, status_ptr)
+ return w_result
-
# Boolean functions
+ def is_canonical_w(self, space):
+ return space.wrap(bool(rmpdec.mpd_iscanonical(self.mpd)))
+ def is_nan_w(self, space):
+ return space.wrap(bool(rmpdec.mpd_isnan(self.mpd)))
def is_qnan_w(self, space):
return space.wrap(bool(rmpdec.mpd_isqnan(self.mpd)))
+ def is_snan_w(self, space):
+ return space.wrap(bool(rmpdec.mpd_issnan(self.mpd)))
def is_infinite_w(self, space):
return space.wrap(bool(rmpdec.mpd_isinfinite(self.mpd)))
+ def is_finite_w(self, space):
+ return space.wrap(bool(rmpdec.mpd_isfinite(self.mpd)))
+ def is_signed_w(self, space):
+ return space.wrap(bool(rmpdec.mpd_issigned(self.mpd)))
+ def is_zero_w(self, space):
+ return space.wrap(bool(rmpdec.mpd_iszero(self.mpd)))
+ # Boolean functions, optional context arg
+ def is_normal_w(self, space, w_context=None):
+ context = interp_context.ensure_context(space, w_context)
+ return space.wrap(bool(rmpdec.mpd_isnormal(self.mpd, context.ctx)))
+ def is_subnormal_w(self, space, w_context=None):
+ context = interp_context.ensure_context(space, w_context)
+ return space.wrap(bool(rmpdec.mpd_issubnormal(self.mpd, context.ctx)))
def as_tuple_w(self, space):
"Return the DecimalTuple representation of a Decimal"
@@ -492,6 +546,24 @@
space.type(w_y))
return w_a, w_b
+def convert_ternop_raise(space, context, w_x, w_y, w_z):
+ w_err, w_a = convert_op(space, context, w_x)
+ if w_err:
+ raise oefmt(space.w_TypeError,
+ "conversion from %N to Decimal is not supported",
+ space.type(w_x))
+ w_err, w_b = convert_op(space, context, w_y)
+ if w_err:
+ raise oefmt(space.w_TypeError,
+ "conversion from %N to Decimal is not supported",
+ space.type(w_y))
+ w_err, w_c = convert_op(space, context, w_z)
+ if w_err:
+ raise oefmt(space.w_TypeError,
+ "conversion from %N to Decimal is not supported",
+ space.type(w_z))
+ return w_a, w_b, w_c
+
# Convert rationals for comparison
def _multiply_by_denominator(space, w_v, w_r, context):
# w_v is not special, w_r is a rational.
@@ -502,7 +574,7 @@
if not vv:
raise OperationError(space.w_MemoryError, space.w_None)
try:
- result = W_Decimal(space)
+ w_result = W_Decimal.allocate(space)
with lltype.scoped_alloc(rmpdec.MPD_CONTEXT_PTR.TO) as maxctx:
rmpdec.mpd_maxcontext(maxctx)
# Prevent Overflow in the following multiplication. The
@@ -514,18 +586,19 @@
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)
+ rmpdec.mpd_qmul(w_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
+ status = rffi.cast(lltype.Signed, status_ptr[0])
+ if status:
+ raise oefmt(space.w_ValueError,
+ "exact conversion for comparison failed")
+ w_result.mpd.c_exp = exp
finally:
rmpdec.mpd_del(vv)
- return space.wrap(result)
+ return w_result
def _numerator_as_decimal(space, w_r, context):
w_numerator = space.getattr(w_r, space.wrap("numerator"))
@@ -609,6 +682,22 @@
return binary_number_method(space, mpd_func, w_other, w_self)
return interp2app(descr_method)
+# Unary function with an optional context arg.
+def unary_method(space, mpd_func, w_self, w_context):
+ self = space.interp_w(W_Decimal, w_self)
+ context = convert_context(space, w_context)
+ w_result = W_Decimal.allocate(space)
+ with context.catch_status(space) as (ctx, status_ptr):
+ mpd_func(w_result.mpd, self.mpd, ctx, status_ptr)
+ return w_result
+
+def make_unary_method(mpd_func_name):
+ mpd_func = getattr(rmpdec, mpd_func_name)
+ @func_renamer('descr_%s' % mpd_func_name)
+ def descr_method(space, w_self, w_context=None):
+ return unary_method(space, mpd_func, w_self, w_context)
+ return interp2app(descr_method)
+
def convert_context(space, w_context):
if w_context is None:
return interp_context.getcontext(space)
@@ -891,14 +980,39 @@
__rmod__ = make_binary_number_method_right('mpd_qrem'),
__rdivmod__ = interp2app(W_Decimal.descr_rdivmod),
__rpow__ = interp2app(W_Decimal.descr_rpow),
+ # Unary functions, optional context arg for conversion errors
+ copy_abs = interp2app(W_Decimal.copy_abs_w),
+ copy_negate = interp2app(W_Decimal.copy_negate_w),
# Unary arithmetic functions, optional context arg
+ exp = make_unary_method('mpd_qexp'),
+ ln = make_unary_method('mpd_qln'),
+ log10 = make_unary_method('mpd_qlog10'),
+ logb = make_unary_method('mpd_qlogb'),
+ logical_invert = make_unary_method('mpd_qinvert'),
+ next_minus = make_unary_method('mpd_qnext_minus'),
+ next_plus = make_unary_method('mpd_qnext_plus'),
+ normalize = make_unary_method('mpd_qreduce'),
+ number_class = interp2app(W_Decimal.number_class_w),
to_integral = interp2app(W_Decimal.to_integral_w),
+ to_integral_exact = interp2app(W_Decimal.to_integral_exact_w),
to_integral_value = interp2app(W_Decimal.to_integral_w),
- to_integral_exact = interp2app(W_Decimal.to_integral_exact_w),
- #
+ sqrt = make_unary_method('mpd_qsqrt'),
+ # Ternary arithmetic functions, optional context arg
+ fma = interp2app(W_Decimal.fma_w),
+ # Boolean functions, no context arg
+ is_canonical = interp2app(W_Decimal.is_canonical_w),
+ is_nan = interp2app(W_Decimal.is_nan_w),
+ is_qnan = interp2app(W_Decimal.is_qnan_w),
+ is_snan = interp2app(W_Decimal.is_snan_w),
+ is_infinite = interp2app(W_Decimal.is_infinite_w),
+ is_finite = interp2app(W_Decimal.is_finite_w),
+ is_signed = interp2app(W_Decimal.is_signed_w),
+ is_zero = interp2app(W_Decimal.is_zero_w),
+ # Boolean functions, optional context arg
+ is_normal = interp2app(W_Decimal.is_normal_w),
+ is_subnormal = interp2app(W_Decimal.is_subnormal_w),
+ # Binary functions, optional context arg for conversion errors
copy_sign = interp2app(W_Decimal.copy_sign_w),
- is_qnan = interp2app(W_Decimal.is_qnan_w),
- is_infinite = interp2app(W_Decimal.is_infinite_w),
#
as_tuple = interp2app(W_Decimal.as_tuple_w),
from_float = interp2app(decimal_from_float_w, as_classmethod=True),
diff --git a/pypy/module/_decimal/test/test_context.py
b/pypy/module/_decimal/test/test_context.py
--- a/pypy/module/_decimal/test/test_context.py
+++ b/pypy/module/_decimal/test/test_context.py
@@ -21,6 +21,9 @@
def assertEqual(space, w_x, w_y):
assert space.eq_w(w_x, w_y)
cls.w_assertEqual = space.wrap(gateway.interp2app(assertEqual))
+ def assertIs(space, w_x, w_y):
+ assert space.is_w(w_x, w_y)
+ cls.w_assertIs = space.wrap(gateway.interp2app(assertIs))
cls.w_assertRaises = space.appexec([], """(): return raises""")
@@ -274,3 +277,84 @@
self.assertEqual(c.is_zero(10), d)
self.assertRaises(TypeError, c.is_zero, '10')
+ def test_implicit_context(self):
+ Decimal = self.decimal.Decimal
+ localcontext = self.decimal.localcontext
+
+ with localcontext() as c:
+ c.prec = 1
+ c.Emax = 1
+ c.Emin = -1
+
+ # abs
+ self.assertEqual(abs(Decimal("-10")), 10)
+ # add
+ self.assertEqual(Decimal("7") + 1, 8)
+ # divide
+ self.assertEqual(Decimal("10") / 5, 2)
+ # divide_int
+ self.assertEqual(Decimal("10") // 7, 1)
+ # fma
+ self.assertEqual(Decimal("1.2").fma(Decimal("0.01"), 1), 1)
+ self.assertIs(Decimal("NaN").fma(7, 1).is_nan(), True)
+ # three arg power
+ self.assertEqual(pow(Decimal(10), 2, 7), 2)
+ # exp
+ self.assertEqual(Decimal("1.01").exp(), 3)
+ # is_normal
+ self.assertIs(Decimal("0.01").is_normal(), False)
+ # is_subnormal
+ self.assertIs(Decimal("0.01").is_subnormal(), True)
+ # ln
+ self.assertEqual(Decimal("20").ln(), 3)
+ # log10
+ self.assertEqual(Decimal("20").log10(), 1)
+ # logb
+ self.assertEqual(Decimal("580").logb(), 2)
+ # logical_invert
+ self.assertEqual(Decimal("10").logical_invert(), 1)
+ # minus
+ self.assertEqual(-Decimal("-10"), 10)
+ # multiply
+ self.assertEqual(Decimal("2") * 4, 8)
+ # next_minus
+ self.assertEqual(Decimal("10").next_minus(), 9)
+ # next_plus
+ self.assertEqual(Decimal("10").next_plus(), Decimal('2E+1'))
+ # normalize
+ self.assertEqual(Decimal("-10").normalize(), Decimal('-1E+1'))
+ # number_class
+ self.assertEqual(Decimal("10").number_class(), '+Normal')
+ # plus
+ self.assertEqual(+Decimal("-1"), -1)
+ # remainder
+ self.assertEqual(Decimal("10") % 7, 3)
+ # subtract
+ self.assertEqual(Decimal("10") - 7, 3)
+ # to_integral_exact
+ self.assertEqual(Decimal("1.12345").to_integral_exact(), 1)
+
+ # Boolean functions
+ self.assertTrue(Decimal("1").is_canonical())
+ self.assertTrue(Decimal("1").is_finite())
+ self.assertTrue(Decimal("1").is_finite())
+ self.assertTrue(Decimal("snan").is_snan())
+ self.assertTrue(Decimal("-1").is_signed())
+ self.assertTrue(Decimal("0").is_zero())
+ self.assertTrue(Decimal("0").is_zero())
+
+ # Copy
+ with localcontext() as c:
+ c.prec = 10000
+ x = 1228 ** 1523
+ y = -Decimal(x)
+
+ z = y.copy_abs()
+ self.assertEqual(z, x)
+
+ z = y.copy_negate()
+ self.assertEqual(z, x)
+
+ z = y.copy_sign(Decimal(1))
+ self.assertEqual(z, x)
+
diff --git a/rpython/rlib/rmpdec.py b/rpython/rlib/rmpdec.py
--- a/rpython/rlib/rmpdec.py
+++ b/rpython/rlib/rmpdec.py
@@ -41,6 +41,7 @@
"mpd_qcopy", "mpd_qncopy", "mpd_setspecial", "mpd_clear_flags",
"mpd_qimport_u32", "mpd_qexport_u32", "mpd_qexport_u16",
"mpd_set_sign", "mpd_set_positive", "mpd_sign", "mpd_qfinalize",
+ "mpd_class",
"mpd_getprec", "mpd_getemin", "mpd_getemax", "mpd_getround",
"mpd_getclamp",
"mpd_qsetprec", "mpd_qsetemin", "mpd_qsetemax", "mpd_qsetround",
"mpd_qsetclamp",
"mpd_maxcontext",
@@ -48,7 +49,7 @@
"mpd_to_sci", "mpd_to_sci_size",
"mpd_iszero", "mpd_isnegative", "mpd_issigned",
"mpd_isfinite", "mpd_isinfinite",
- "mpd_isnormal", "mpd_issubnormal", "mpd_isspecial",
+ "mpd_isnormal", "mpd_issubnormal", "mpd_isspecial", "mpd_iscanonical",
"mpd_isnan", "mpd_issnan", "mpd_isqnan",
"mpd_qcmp", "mpd_qcompare", "mpd_qcompare_signal",
"mpd_qmin", "mpd_qmax", "mpd_qmin_mag", "mpd_qmax_mag",
@@ -58,9 +59,10 @@
"mpd_qadd", "mpd_qsub", "mpd_qmul", "mpd_qdiv", "mpd_qdivint",
"mpd_qrem", "mpd_qrem_near", "mpd_qdivmod", "mpd_qpow", "mpd_qpowmod",
"mpd_qfma",
- "mpd_qexp", "mpd_qln", "mpd_qlog10", "mpd_qsqrt", "mpd_qinvert",
+ "mpd_qexp", "mpd_qln", "mpd_qlog10", "mpd_qlogb",
+ "mpd_qsqrt", "mpd_qinvert",
"mpd_qand", "mpd_qor", "mpd_qxor",
- "mpd_qcopy_sign",
+ "mpd_qcopy_sign", "mpd_qcopy_abs", "mpd_qcopy_negate",
"mpd_qround_to_int", "mpd_qround_to_intx",
],
compile_extra=compile_extra,
@@ -187,6 +189,8 @@
'mpd_sign', [MPD_PTR], rffi.UCHAR)
mpd_qfinalize = external(
'mpd_qfinalize', [MPD_PTR, MPD_CONTEXT_PTR, rffi.UINTP], lltype.Void)
+mpd_class = external(
+ 'mpd_class', [MPD_PTR, MPD_CONTEXT_PTR], rffi.CCHARP)
# Context operations
mpd_getprec = external(
@@ -247,6 +251,8 @@
'mpd_issubnormal', [MPD_PTR, MPD_CONTEXT_PTR], rffi.INT)
mpd_isspecial = external(
'mpd_isspecial', [MPD_PTR], rffi.INT)
+mpd_iscanonical = external(
+ 'mpd_iscanonical', [MPD_PTR], rffi.INT)
mpd_isnan = external(
'mpd_isnan', [MPD_PTR], rffi.INT)
mpd_issnan = external(
@@ -358,6 +364,9 @@
mpd_qlog10 = external(
'mpd_qlog10',
[MPD_PTR, MPD_PTR, MPD_CONTEXT_PTR, rffi.UINTP], lltype.Void)
+mpd_qlogb = external(
+ 'mpd_qlogb',
+ [MPD_PTR, MPD_PTR, MPD_CONTEXT_PTR, rffi.UINTP], lltype.Void)
mpd_qsqrt = external(
'mpd_qsqrt',
[MPD_PTR, MPD_PTR, MPD_CONTEXT_PTR, rffi.UINTP], lltype.Void)
@@ -369,6 +378,14 @@
'mpd_qcopy_sign',
[MPD_PTR, MPD_PTR, MPD_PTR, rffi.UINTP],
lltype.Void)
+mpd_qcopy_abs = external(
+ 'mpd_qcopy_abs',
+ [MPD_PTR, MPD_PTR, rffi.UINTP],
+ lltype.Void)
+mpd_qcopy_negate = external(
+ 'mpd_qcopy_negate',
+ [MPD_PTR, MPD_PTR, rffi.UINTP],
+ lltype.Void)
mpd_qround_to_int = external(
'mpd_qround_to_int', [MPD_PTR, MPD_PTR, MPD_CONTEXT_PTR, rffi.UINTP],
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit