[pypy-commit] pypy decimal-libmpdec: Add Decimal.as_tuple()

amauryfa noreply at buildbot.pypy.org
Sat May 17 11:56:48 CEST 2014


Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: decimal-libmpdec
Changeset: r71551:b6fe176810c7
Date: 2014-05-11 20:31 +0200
http://bitbucket.org/pypy/pypy/changeset/b6fe176810c7/

Log:	Add Decimal.as_tuple()

diff --git a/pypy/module/_decimal/interp_context.py b/pypy/module/_decimal/interp_context.py
--- a/pypy/module/_decimal/interp_context.py
+++ b/pypy/module/_decimal/interp_context.py
@@ -61,6 +61,10 @@
                             w_MutableMapping]),
             space.newdict())
 
+        self.W_DecimalTuple = space.call_method(
+            w_collections, "namedtuple",
+            space.wrap("DecimalTuple"), space.wrap("sign digits exponent"))
+
 def state_get(space):
     return space.fromcache(State)
 
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
@@ -315,6 +315,53 @@
     def is_infinite_w(self, space):
         return space.wrap(bool(rmpdec.mpd_isinfinite(self.mpd)))
 
+    def as_tuple_w(self, space):
+        "Return the DecimalTuple representation of a Decimal"
+        w_sign = space.wrap(rmpdec.mpd_sign(self.mpd))
+        if rmpdec.mpd_isinfinite(self.mpd):
+            w_expt = space.wrap("F")
+            # decimal.py has non-compliant infinity payloads.
+            w_coeff = space.newtuple([space.wrap(0)])
+        else:
+            if rmpdec.mpd_isnan(self.mpd):
+                if rmpdec.mpd_issnan(self.mpd):
+                    w_expt = space.wrap("N")
+                else:
+                    w_expt = space.wrap("n")
+            else:
+                w_expt = space.wrap(self.mpd.c_exp)
+
+            if self.mpd.c_len > 0:
+                # coefficient is defined
+
+                # make an integer
+                # XXX this should be done in C...
+                x = rmpdec.mpd_qncopy(self.mpd)
+                if not x:
+                    raise OperationError(space.w_MemoryError, space.w_None)
+                try:
+                    x.c_exp = 0
+                    # clear NaN and sign
+                    rmpdec.mpd_clear_flags(x)
+                    intstring = rmpdec.mpd_to_sci(x, 1)
+                finally:
+                    rmpdec.mpd_del(x)
+                if not intstring:
+                    raise OperationError(space.w_MemoryError, space.w_None)
+                try:
+                    digits = rffi.charp2str(intstring)
+                finally:
+                    rmpdec.mpd_free(intstring)
+                w_coeff = space.newtuple([
+                        space.wrap(ord(d) - ord('0'))
+                        for d in digits])
+            else:
+                w_coeff = space.newtuple([])
+
+        return space.call_function(
+            interp_context.state_get(space).W_DecimalTuple,
+            w_sign, w_coeff, w_expt)
+
 
 # Helper functions for arithmetic conversions
 def convert_op(space, context, w_value):
@@ -643,4 +690,6 @@
     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),
     )
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
@@ -712,3 +712,49 @@
         d = Decimal(1).copy_sign(Decimal(-2))
         assert Decimal(1).copy_sign(-2) == d
         raises(TypeError, Decimal(1).copy_sign, '-2')
+
+    def test_as_tuple(self):
+        Decimal = self.decimal.Decimal
+
+        #with zero
+        d = Decimal(0)
+        assert d.as_tuple() == (0, (0,), 0) 
+
+        #int
+        d = Decimal(-45)
+        assert d.as_tuple() == (1, (4, 5), 0) 
+
+        #complicated string
+        d = Decimal("-4.34913534E-17")
+        assert d.as_tuple() == (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) 
+
+        # The '0' coefficient is implementation specific to decimal.py.
+        # It has no meaning in the C-version and is ignored there.
+        d = Decimal("Infinity")
+        assert d.as_tuple() == (0, (0,), 'F') 
+
+        #leading zeros in coefficient should be stripped
+        d = Decimal( (0, (0, 0, 4, 0, 5, 3, 4), -2) )
+        assert d.as_tuple() == (0, (4, 0, 5, 3, 4), -2) 
+        d = Decimal( (1, (0, 0, 0), 37) )
+        assert d.as_tuple() == (1, (0,), 37)
+        d = Decimal( (1, (), 37) )
+        assert d.as_tuple() == (1, (0,), 37)
+
+        #leading zeros in NaN diagnostic info should be stripped
+        d = Decimal( (0, (0, 0, 4, 0, 5, 3, 4), 'n') )
+        assert d.as_tuple() == (0, (4, 0, 5, 3, 4), 'n') 
+        d = Decimal( (1, (0, 0, 0), 'N') )
+        assert d.as_tuple() == (1, (), 'N') 
+        d = Decimal( (1, (), 'n') )
+        assert d.as_tuple() == (1, (), 'n') 
+
+        # For infinities, decimal.py has always silently accepted any
+        # coefficient tuple.
+        d = Decimal( (0, (0,), 'F') )
+        assert d.as_tuple() == (0, (0,), 'F')
+        d = Decimal( (0, (4, 5, 3, 4), 'F') )
+        assert d.as_tuple() == (0, (0,), 'F')
+        d = Decimal( (1, (0, 2, 7, 1), 'F') )
+        assert d.as_tuple() == (1, (0,), 'F')
+
diff --git a/rpython/rlib/rmpdec.py b/rpython/rlib/rmpdec.py
--- a/rpython/rlib/rmpdec.py
+++ b/rpython/rlib/rmpdec.py
@@ -36,13 +36,14 @@
                            libdir.join('memory.c'),
                            ],
     export_symbols=[
-        "mpd_qset_ssize", "mpd_qset_uint", "mpd_qset_string", "mpd_qcopy", "mpd_setspecial",
+        "mpd_qset_ssize", "mpd_qset_uint", "mpd_qset_string",
+        "mpd_qcopy", "mpd_qncopy", "mpd_setspecial", "mpd_clear_flags",
         "mpd_qimport_u32", "mpd_qexport_u32", "mpd_qexport_u16",
-        "mpd_set_sign", "mpd_qfinalize",
+        "mpd_set_sign", "mpd_sign", "mpd_qfinalize",
         "mpd_getprec", "mpd_getemin",  "mpd_getemax", "mpd_getround", "mpd_getclamp",
         "mpd_qsetprec", "mpd_qsetemin",  "mpd_qsetemax", "mpd_qsetround", "mpd_qsetclamp",
         "mpd_maxcontext",
-        "mpd_qnew",
+        "mpd_qnew", "mpd_del",
         "mpd_to_sci", "mpd_to_sci_size",
         "mpd_iszero", "mpd_isnegative", "mpd_isinfinite", "mpd_isspecial",
         "mpd_isnan", "mpd_issnan", "mpd_isqnan",
@@ -156,10 +157,16 @@
         MPD_PTR, rffi.UINTP], rffi.SIZE_T)
 mpd_qcopy = external(
     'mpd_qcopy', [MPD_PTR, MPD_PTR, rffi.UINTP], rffi.INT)
+mpd_qncopy = external(
+    'mpd_qncopy', [MPD_PTR], MPD_PTR)
 mpd_setspecial = external(
     'mpd_setspecial', [MPD_PTR, rffi.UCHAR, rffi.UCHAR], lltype.Void)
 mpd_set_sign = external(
     'mpd_set_sign', [MPD_PTR, rffi.UCHAR], lltype.Void)
+mpd_clear_flags = external(
+    'mpd_clear_flags', [MPD_PTR], lltype.Void)
+mpd_sign = external(
+    'mpd_sign', [MPD_PTR], rffi.UCHAR)
 mpd_qfinalize = external(
     'mpd_qfinalize', [MPD_PTR, MPD_CONTEXT_PTR, rffi.UINTP], lltype.Void)
 
@@ -191,6 +198,8 @@
 
 mpd_qnew = external(
     'mpd_qnew', [], MPD_PTR)
+mpd_del = external(
+    'mpd_del', [MPD_PTR], lltype.Void)
 mpd_free = external(
     'mpd_free', [rffi.VOIDP], lltype.Void, macro=True)
 


More information about the pypy-commit mailing list