Author: Amaury Forgeot d'Arc <amaur...@gmail.com>
Branch: decimal-libmpdec
Changeset: r71464:502a9fa6ddd6
Date: 2014-05-10 23:03 +0200
http://bitbucket.org/pypy/pypy/changeset/502a9fa6ddd6/

Log:    Add int() conversion, support for trunc(), round()...

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
@@ -94,6 +94,72 @@
             w_s = self.descr_str(space)
         return space.call_function(space.w_float, w_s)
 
+    def to_long(self, space, context, round):
+        if rmpdec.mpd_isspecial(self.mpd):
+            if rmpdec.mpd_isnan(self.mpd):
+                raise oefmt(space.w_ValueError,
+                            "cannot convert NaN to integer")
+            else:
+                raise oefmt(space.w_OverflowError,
+                            "cannot convert Infinity to integer")
+
+        w_x = W_Decimal.allocate(space)
+        w_tempctx = context.copy_w(space)
+        rffi.setintfield(w_tempctx.ctx, 'c_round', round)
+        with context.catch_status(space) as (ctx, status_ptr):
+            # We round with the temporary context, but set status and
+            # raise errors on the global one.
+            rmpdec.mpd_qround_to_int(w_x.mpd, self.mpd,
+                                     w_tempctx.ctx, status_ptr)
+
+            # XXX mpd_qexport_u64 would be faster...
+            T = rffi.CArrayPtr(rffi.USHORTP).TO
+            with lltype.scoped_alloc(T, 1, zero=True) as digits_ptr:
+                n = rmpdec.mpd_qexport_u16(
+                    digits_ptr, 0, 0x10000,
+                    w_x.mpd, status_ptr)
+                if n == rmpdec.MPD_SIZE_MAX:
+                    raise OperationError(space.w_MemoryError, space.w_None)
+                try:
+                    char_ptr = rffi.cast(rffi.CCHARP, digits_ptr[0])
+                    size = rffi.cast(lltype.Signed, n) * 2
+                    s = rffi.charpsize2str(char_ptr, size)
+                finally:
+                    rmpdec.mpd_free(digits_ptr[0])
+            bigint = rbigint.rbigint.frombytes(
+                s, byteorder=rbigint.BYTEORDER, signed=False)
+        if rmpdec.mpd_isnegative(w_x.mpd) and not rmpdec.mpd_iszero(w_x.mpd):
+            bigint = bigint.neg()
+        return space.newlong_from_rbigint(bigint)
+
+    def descr_int(self, space):
+        context = interp_context.getcontext(space)
+        return self.to_long(space, context, rmpdec.MPD_ROUND_DOWN)
+        
+    def descr_floor(self, space):
+        context = interp_context.getcontext(space)
+        return self.to_long(space, context, rmpdec.MPD_ROUND_FLOOR)
+        
+    def descr_ceil(self, space):
+        context = interp_context.getcontext(space)
+        return self.to_long(space, context, rmpdec.MPD_ROUND_CEILING)
+
+    def descr_round(self, space, w_x=None):
+        context = interp_context.getcontext(space)
+        if not w_x:
+            return self.to_long(space, context, rmpdec.MPD_ROUND_HALF_EVEN)
+        x = space.int_w(w_x)
+        w_result = W_Decimal.allocate(space)
+        w_q = decimal_from_ssize(space, None, 1, context, exact=False)
+        if x == rmpdec.MPD_SSIZE_MIN:
+            w_q.mpd.c_exp = rmpdec.MPD_SSIZE_MAX
+        else:
+            w_q.mpd.c_exp = -x
+        with context.catch_status(space) as (ctx, status_ptr):
+            rmpdec.mpd_qquantize(w_result.mpd, self.mpd, w_q.mpd,
+                                 ctx, status_ptr)
+        return w_result
+
     def compare(self, space, w_other, op):
         if not isinstance(w_other, W_Decimal):  # So far
             return space.w_NotImplemented
@@ -388,6 +454,10 @@
     __repr__ = interp2app(W_Decimal.descr_repr),
     __bool__ = interp2app(W_Decimal.descr_bool),
     __float__ = interp2app(W_Decimal.descr_float),
+    __int__ = interp2app(W_Decimal.descr_int),
+    __floor__ = interp2app(W_Decimal.descr_floor),
+    __ceil__ = interp2app(W_Decimal.descr_ceil),
+    __round__ = interp2app(W_Decimal.descr_round),
     __eq__ = interp2app(W_Decimal.descr_eq),
     #
     __add__ = interp2app(W_Decimal.descr_add),
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
@@ -333,17 +333,21 @@
     def test_tonum_methods(self):
         #Test float and int methods.
         Decimal = self.decimal.Decimal
+        InvalidOperation = self.decimal.InvalidOperation
+        self.decimal.getcontext().traps[InvalidOperation] = False
+
+        import math
 
         d1 = Decimal('66')
         d2 = Decimal('15.32')
 
         #int
-        int(d1) == 66
-        int(d2) == 15
+        assert int(d1) == 66
+        assert int(d2) == 15
 
         #float
-        float(d1) == 66
-        float(d2) == 15.32
+        assert float(d1) == 66
+        assert float(d2) == 15.32
 
         #floor
         test_pairs = [
diff --git a/rpython/rlib/rmpdec.py b/rpython/rlib/rmpdec.py
--- a/rpython/rlib/rmpdec.py
+++ b/rpython/rlib/rmpdec.py
@@ -37,6 +37,7 @@
                            ],
     export_symbols=[
         "mpd_qset_ssize", "mpd_qset_uint", "mpd_qset_string", "mpd_qcopy", 
"mpd_setspecial",
+        "mpd_qimport_u32", "mpd_qexport_u32", "mpd_qexport_u16",
         "mpd_set_sign", "mpd_qfinalize",
         "mpd_getprec", "mpd_getemin",  "mpd_getemax", "mpd_getround", 
"mpd_getclamp",
         "mpd_qsetprec", "mpd_qsetemin",  "mpd_qsetemax", "mpd_qsetround", 
"mpd_qsetclamp",
@@ -45,7 +46,7 @@
         "mpd_to_sci", "mpd_to_sci_size",
         "mpd_iszero", "mpd_isnegative", "mpd_isinfinite", "mpd_isspecial",
         "mpd_isnan", "mpd_issnan", "mpd_isqnan",
-        "mpd_qcmp",
+        "mpd_qcmp", "mpd_qquantize",
         "mpd_qpow", "mpd_qadd", "mpd_qsub", "mpd_qmul", "mpd_qdiv",
         "mpd_qround_to_int",
         ],
@@ -81,6 +82,9 @@
         'MPD_IEEE_CONTEXT_MAX_BITS')
     MPD_MAX_PREC = platform.ConstantInteger('MPD_MAX_PREC')
     MPD_MAX_SIGNAL_LIST = platform.ConstantInteger('MPD_MAX_SIGNAL_LIST')
+    MPD_SIZE_MAX = platform.ConstantInteger('MPD_SIZE_MAX')
+    MPD_SSIZE_MAX = platform.ConstantInteger('MPD_SSIZE_MAX')
+    MPD_SSIZE_MIN = platform.ConstantInteger('MPD_SSIZE_MIN')
 
     # Flags
     MPD_POS = platform.ConstantInteger('MPD_POS')
@@ -136,8 +140,17 @@
 mpd_qset_string = external(
     'mpd_qset_string', [MPD_PTR, rffi.CCHARP, MPD_CONTEXT_PTR, rffi.UINTP], 
lltype.Void)
 mpd_qimport_u32 = external(
-    'mpd_qimport_u32', [MPD_PTR, rffi.UINTP, rffi.SIZE_T,
-                        rffi.UCHAR, rffi.UINT, MPD_CONTEXT_PTR, rffi.UINTP], 
rffi.SIZE_T)
+    'mpd_qimport_u32', [
+        MPD_PTR, rffi.UINTP, rffi.SIZE_T,
+        rffi.UCHAR, rffi.UINT, MPD_CONTEXT_PTR, rffi.UINTP], rffi.SIZE_T)
+mpd_qexport_u32 = external(
+    'mpd_qexport_u32', [
+        rffi.CArrayPtr(rffi.UINTP), rffi.SIZE_T, rffi.UINT,
+        MPD_PTR, rffi.UINTP], rffi.SIZE_T)
+mpd_qexport_u16 = external(
+    'mpd_qexport_u16', [
+        rffi.CArrayPtr(rffi.USHORTP), rffi.SIZE_T, rffi.UINT,
+        MPD_PTR, rffi.UINTP], rffi.SIZE_T)
 mpd_qcopy = external(
     'mpd_qcopy', [MPD_PTR, MPD_PTR, rffi.UINTP], rffi.INT)
 mpd_setspecial = external(
@@ -204,6 +217,9 @@
     'mpd_isqnan', [MPD_PTR], rffi.INT)
 mpd_qcmp = external(
     'mpd_qcmp', [MPD_PTR, MPD_PTR, rffi.UINTP], rffi.INT)
+mpd_qquantize = external(
+    'mpd_qquantize', [MPD_PTR, MPD_PTR, MPD_PTR, MPD_CONTEXT_PTR, rffi.UINTP],
+    lltype.Void)
 
 mpd_qpow = external(
     'mpd_qpow',
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to