[pypy-commit] pypy decimal-libmpdec: Lots of progress in context.create_decimal().

amauryfa noreply at buildbot.pypy.org
Sun May 11 00:27:48 CEST 2014


Author: Amaury Forgeot d'Arc <amauryfa at gmail.com>
Branch: decimal-libmpdec
Changeset: r71458:f8e3661a9e69
Date: 2014-05-10 12:13 +0200
http://bitbucket.org/pypy/pypy/changeset/f8e3661a9e69/

Log:	Lots of progress in context.create_decimal().

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
@@ -67,12 +67,28 @@
 ROUND_CONSTANTS = unrolling_iterable([
         (name, getattr(rmpdec, 'MPD_' + name))
         for name in rmpdec.ROUND_CONSTANTS])
+DEC_DFLT_EMAX = 999999
+DEC_DFLT_EMIN = -999999
 
 class W_Context(W_Root):
     def __init__(self, space):
         self.ctx = lltype.malloc(rmpdec.MPD_CONTEXT_PTR.TO, flavor='raw',
                                  zero=True,
                                  track_allocation=False)
+        # Default context
+        self.ctx.c_prec = 28
+        self.ctx.c_emax = DEC_DFLT_EMAX
+        self.ctx.c_emin = DEC_DFLT_EMIN
+        rffi.setintfield(self.ctx, 'c_traps',
+                         (rmpdec.MPD_IEEE_Invalid_operation|
+                          rmpdec.MPD_Division_by_zero|
+                          rmpdec.MPD_Overflow))
+        rffi.setintfield(self.ctx, 'c_status', 0)
+        rffi.setintfield(self.ctx, 'c_newtrap', 0)
+        rffi.setintfield(self.ctx, 'c_round', rmpdec.MPD_ROUND_HALF_EVEN)
+        rffi.setintfield(self.ctx, 'c_clamp', 0)
+        rffi.setintfield(self.ctx, 'c_allcr', 1)
+        
         self.w_flags = new_signal_dict(
             space, lltype.direct_fieldptr(self.ctx, 'c_status'))
         self.w_traps = new_signal_dict(
@@ -97,7 +113,8 @@
 
     def copy_w(self, space):
         w_copy = W_Context(space)
-        # XXX incomplete
+        rffi.structcopy(w_copy.ctx, self.ctx)
+        w_copy.capitals = self.capitals
         return w_copy
 
     def get_prec(self, space):
@@ -154,6 +171,11 @@
             raise oefmt(space.w_ValueError,
                         "valid values for clamp are 0 or 1")
 
+    def create_decimal_w(self, space, w_value=None):
+        from pypy.module._decimal import interp_decimal
+        return interp_decimal.decimal_from_object(
+            space, None, w_value, self, exact=False)
+
 
 def descr_new_context(space, w_subtype, __args__):
     w_result = space.allocate_instance(W_Context, w_subtype)
@@ -162,7 +184,8 @@
 
 W_Context.typedef = TypeDef(
     'Context',
-    copy=interp2app(W_Context.copy_w),
+    __new__ = interp2app(descr_new_context),
+    # Attributes
     flags=interp_attrproperty_w('w_flags', W_Context),
     traps=interp_attrproperty_w('w_traps', W_Context),
     prec=GetSetProperty(W_Context.get_prec, W_Context.set_prec),
@@ -170,7 +193,9 @@
     Emin=GetSetProperty(W_Context.get_emin, W_Context.set_emin),
     Emax=GetSetProperty(W_Context.get_emax, W_Context.set_emax),
     clamp=GetSetProperty(W_Context.get_clamp, W_Context.set_clamp),
-    __new__ = interp2app(descr_new_context),
+    #
+    copy=interp2app(W_Context.copy_w),
+    create_decimal=interp2app(W_Context.create_decimal_w),
     )
 
 
@@ -224,6 +249,7 @@
                     self.mpd, rmpdec.MPD_Invalid_operation, self.status_ptr)
         status = rffi.cast(lltype.Signed, self.status_ptr[0])
         lltype.free(self.status_ptr, flavor='raw')
-        status &= rmpdec.MPD_Errors
+        if self.exact:
+            status &= rmpdec.MPD_Errors
         # May raise a DecimalException
         self.context.addstatus(self.space, status)
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
@@ -36,18 +36,23 @@
         if self.data:
             lltype.free(self.data, flavor='raw')
 
-    def apply(self, context, w_subtype=None):
-        # Apply the context to the input operand. Return a new W_Decimal.
-        if subtype:
+    @staticmethod
+    def allocate(space, w_subtype=None):
+        if w_subtype:
             w_result = space.allocate_instance(W_Decimal, w_subtype)
             W_Decimal.__init__(w_result, space)
         else:
             w_result = W_Decimal(space)
+        return w_result
+
+    def apply(self, space, context, w_subtype=None):
+        # Apply the context to the input operand. Return a new W_Decimal.
+        w_result = W_Decimal.allocate(space, w_subtype)
         with lltype.scoped_alloc(rffi.CArrayPtr(rffi.UINT).TO, 1) as status_ptr:
             rmpdec.mpd_qcopy(w_result.mpd, self.mpd, status_ptr)
-            context.addstatus(self.space, status_ptr[0])
+            context.addstatus(space, status_ptr[0])
             rmpdec.mpd_qfinalize(w_result.mpd, context.ctx, status_ptr)
-            context.addstatus(self.space, status_ptr[0])
+            context.addstatus(space, status_ptr[0])
         return w_result
 
     def descr_str(self, space):
@@ -101,17 +106,14 @@
 
 # Constructors
 def decimal_from_ssize(space, w_subtype, value, context, exact=True):
-    w_result = space.allocate_instance(W_Decimal, w_subtype)
-    W_Decimal.__init__(w_result, space)
+    w_result = W_Decimal.allocate(space, w_subtype)
     with interp_context.ConvContext(
             space, w_result.mpd, context, exact) as (ctx, status_ptr):
         rmpdec.mpd_qset_ssize(w_result.mpd, value, ctx, status_ptr)
     return w_result
 
 def decimal_from_cstring(space, w_subtype, value, context, exact=True):
-    w_result = space.allocate_instance(W_Decimal, w_subtype)
-    W_Decimal.__init__(w_result, space)
-
+    w_result = W_Decimal.allocate(space, w_subtype)
     with interp_context.ConvContext(
             space, w_result.mpd, context, exact) as (ctx, status_ptr):
         rmpdec.mpd_qset_string(w_result.mpd, value, ctx, status_ptr)
@@ -127,8 +129,7 @@
     return decimal_from_cstring(space, w_subtype, s, context, exact=exact)
 
 def decimal_from_bigint(space, w_subtype, value, context, exact=True):
-    w_result = space.allocate_instance(W_Decimal, w_subtype)
-    W_Decimal.__init__(w_result, space)
+    w_result = W_Decimal.allocate(space, w_subtype)
 
     with interp_context.ConvContext(
             space, w_result.mpd, context, exact) as (ctx, status_ptr):
@@ -235,37 +236,34 @@
     if exact:
         if space.is_w(w_subtype, space.gettypeobject(W_Decimal.typedef)):
             return w_value
-        w_result = space.allocate_instance(W_Decimal, w_subtype)
-        W_Decimal.__init__(w_result, space)
+        w_result = W_Decimal.allocate(space, w_subtype)
         with interp_context.ConvContext(
                 space, w_result.mpd, context, exact) as (ctx, status_ptr):
             rmpdec.mpd_qcopy(w_result.mpd, w_value.mpd, status_ptr)
         return w_result
     else:
         if (rmpdec.mpd_isnan(w_value.mpd) and
-            w_value.mpd.digits > (context.ctx.prec - context.ctx.clamp)):
+            w_value.mpd.c_digits > (context.ctx.c_prec - context.ctx.c_clamp)):
             # Special case: too many NaN payload digits
             context.addstatus(space, rmpdec.MPD_Conversion_syntax)
-            w_result = space.allocate_instance(W_Decimal, w_subtype)
-            W_Decimal.__init__(w_result, space)
+            w_result = W_Decimal.allocate(space, w_subtype)
             rmpdec.mpd_setspecial(w_result.mpd, rmpdec.MPD_POS, rmpdec.MPD_NAN)
+            return w_result
         else:
-            return w_value.apply(context)
+            return w_value.apply(space, context)
 
 def decimal_from_float(space, w_subtype, w_value, context, exact=True):
     value = space.float_w(w_value)
     sign = 0 if rfloat.copysign(1.0, value) == 1.0 else 1
 
     if rfloat.isnan(value):
-        w_result = space.allocate_instance(W_Decimal, w_subtype)
-        W_Decimal.__init__(w_result, space)
+        w_result = W_Decimal.allocate(space, w_subtype)
         # decimal.py calls repr(float(+-nan)), which always gives a
         # positive result.
         rmpdec.mpd_setspecial(w_result.mpd, rmpdec.MPD_POS, rmpdec.MPD_NAN)
         return w_result
     if rfloat.isinf(value):
-        w_result = space.allocate_instance(W_Decimal, w_subtype)
-        W_Decimal.__init__(w_result, space)
+        w_result = W_Decimal.allocate(space, w_subtype)
         rmpdec.mpd_setspecial(w_result.mpd, sign, rmpdec.MPD_INF)
         return w_result
 
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
@@ -203,3 +203,91 @@
         for i in range(200):
             x = self.random_float()
             assert x == float(Decimal(x)) # roundtrip
+
+    def test_explicit_context_create_decimal(self):
+        Decimal = self.decimal.Decimal
+        InvalidOperation = self.decimal.InvalidOperation
+        Rounded = self.decimal.Rounded
+
+        nc = self.decimal.getcontext().copy()
+        nc.prec = 3
+        nc.traps[InvalidOperation] = False
+        nc.traps[self.decimal.Overflow] = False
+        nc.traps[self.decimal.DivisionByZero] = False
+
+        # empty
+        d = Decimal()
+        assert str(d) == '0'
+        d = nc.create_decimal()
+        assert str(d) == '0'
+
+        # from None
+        raises(TypeError, nc.create_decimal, None)
+
+        # from int
+        d = nc.create_decimal(456)
+        assert isinstance(d, Decimal)
+        assert nc.create_decimal(45678) == nc.create_decimal('457E+2')
+
+        # from string
+        d = Decimal('456789')
+        assert str(d) == '456789'
+        d = nc.create_decimal('456789')
+        assert str(d) == '4.57E+5'
+        # leading and trailing whitespace should result in a NaN;
+        # spaces are already checked in Cowlishaw's test-suite, so
+        # here we just check that a trailing newline results in a NaN
+        assert str(nc.create_decimal('3.14\n')) == 'NaN'
+
+        # from tuples
+        d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
+        assert str(d) == '-4.34913534E-17'
+        d = nc.create_decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
+        assert str(d) == '-4.35E-17'
+
+        # from Decimal
+        prevdec = Decimal(500000123)
+        d = Decimal(prevdec)
+        assert str(d) == '500000123'
+        d = nc.create_decimal(prevdec)
+        assert str(d) == '5.00E+8'
+
+        # more integers
+        nc.prec = 28
+        nc.traps[InvalidOperation] = True
+
+        for v in [-2**63-1, -2**63, -2**31-1, -2**31, 0,
+                   2**31-1, 2**31, 2**63-1, 2**63]:
+            d = nc.create_decimal(v)
+            assert isinstance(d, Decimal)
+            assert str(d) == str(v)
+
+        nc.prec = 3
+        nc.traps[Rounded] = True
+        raises(Rounded, nc.create_decimal, 1234)
+
+        # from string
+        nc.prec = 28
+        assert str(nc.create_decimal('0E-017')) == '0E-17'
+        assert str(nc.create_decimal('45')) == '45'
+        assert str(nc.create_decimal('-Inf')) == '-Infinity'
+        assert str(nc.create_decimal('NaN123')) == 'NaN123'
+
+        # invalid arguments
+        raises(InvalidOperation, nc.create_decimal, "xyz")
+        raises(ValueError, nc.create_decimal, (1, "xyz", -25))
+        raises(TypeError, nc.create_decimal, "1234", "5678")
+
+        # too many NaN payload digits
+        nc.prec = 3
+        raises(InvalidOperation, nc.create_decimal, 'NaN12345')
+        raises(InvalidOperation, nc.create_decimal, Decimal('NaN12345'))
+
+        nc.traps[InvalidOperation] = False
+        assert str(nc.create_decimal('NaN12345')) == 'NaN'
+        assert nc.flags[InvalidOperation]
+
+        nc.flags[InvalidOperation] = False
+        assert str(nc.create_decimal(Decimal('NaN12345'))) == 'NaN'
+        assert nc.flags[InvalidOperation]
+
diff --git a/rpython/rlib/rmpdec.py b/rpython/rlib/rmpdec.py
--- a/rpython/rlib/rmpdec.py
+++ b/rpython/rlib/rmpdec.py
@@ -43,10 +43,11 @@
         "mpd_maxcontext",
         "mpd_qnew",
         "mpd_to_sci_size",
-        "mpd_iszero", "mpd_isnegative", "mpd_isinfinite",
+        "mpd_iszero", "mpd_isnegative", "mpd_isinfinite", "mpd_isspecial",
         "mpd_isnan", "mpd_issnan", "mpd_isqnan",
         "mpd_qcmp",
         "mpd_qpow", "mpd_qmul",
+        "mpd_qround_to_int",
         ],
     compile_extra=compile_extra,
     libraries=['m'],
@@ -104,8 +105,15 @@
                              ('data', MPD_UINT_PTR),
                              ])
     MPD_CONTEXT_T = platform.Struct('mpd_context_t',
-                                    [('traps', rffi.UINT),
+                                    [('prec', lltype.Signed),
+                                     ('emax', lltype.Signed),
+                                     ('emin', lltype.Signed),
+                                     ('traps', rffi.UINT),
                                      ('status', rffi.UINT),
+                                     ('newtrap', rffi.UINT),
+                                     ('round', lltype.Signed),
+                                     ('clamp', lltype.Signed),
+                                     ('allcr', lltype.Signed),
                                      ])
 
 
@@ -183,6 +191,8 @@
     'mpd_isnegative', [MPD_PTR], rffi.INT)
 mpd_isinfinite = external(
     'mpd_isinfinite', [MPD_PTR], rffi.INT)
+mpd_isspecial = external(
+    'mpd_isspecial', [MPD_PTR], rffi.INT)
 mpd_isnan = external(
     'mpd_isnan', [MPD_PTR], rffi.INT)
 mpd_issnan = external(
@@ -200,3 +210,7 @@
     'mpd_qmul',
     [MPD_PTR, MPD_PTR, MPD_PTR, MPD_CONTEXT_PTR, rffi.UINTP],
     lltype.Void)
+
+mpd_qround_to_int = external(
+    'mpd_qround_to_int', [MPD_PTR, MPD_PTR, MPD_CONTEXT_PTR, rffi.UINTP],
+    lltype.Void)


More information about the pypy-commit mailing list