Author: Amaury Forgeot d'Arc <[email protected]>
Branch: decimal-libmpdec
Changeset: r71457:2277990d7939
Date: 2014-05-09 23:12 +0200
http://bitbucket.org/pypy/pypy/changeset/2277990d7939/

Log:    One more test, progress!

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
@@ -1,4 +1,4 @@
-from rpython.rlib import rmpdec, rarithmetic, rbigint
+from rpython.rlib import rmpdec, rarithmetic, rbigint, rfloat
 from rpython.rlib.rstring import StringBuilder
 from rpython.rtyper.lltypesystem import rffi, lltype
 from pypy.interpreter.baseobjspace import W_Root
@@ -20,7 +20,8 @@
 
     def __init__(self, space):
         self.mpd = lltype.malloc(rmpdec.MPD_PTR.TO, flavor='raw')
-        self.data = lltype.malloc(rffi.UINTP.TO, DEC_MINALLOC, flavor='raw')
+        self.data = lltype.malloc(rmpdec.MPD_UINT_PTR.TO,
+                                  DEC_MINALLOC, flavor='raw')
         rffi.setintfield(self.mpd, 'c_flags',
                          rmpdec.MPD_STATIC | rmpdec.MPD_STATIC_DATA)
         self.mpd.c_exp = 0
@@ -43,9 +44,9 @@
         else:
             w_result = W_Decimal(space)
         with lltype.scoped_alloc(rffi.CArrayPtr(rffi.UINT).TO, 1) as 
status_ptr:
-            rpmdec.mpd_qcopy(w_result.mpd, self.mpd, status_ptr)
+            rmpdec.mpd_qcopy(w_result.mpd, self.mpd, status_ptr)
             context.addstatus(self.space, status_ptr[0])
-            rpmdec.mpd_qfinalize(w_result.mpd, context.ctx, status_ptr)
+            rmpdec.mpd_qfinalize(w_result.mpd, context.ctx, status_ptr)
             context.addstatus(self.space, status_ptr[0])
         return w_result
 
@@ -65,6 +66,19 @@
     def descr_bool(self, space):
         return space.wrap(not rmpdec.mpd_iszero(self.mpd))
 
+    def descr_float(self, space):
+        if rmpdec.mpd_isnan(self.mpd):
+            if rmpdec.mpd_issnan(self.mpd):
+                raise oefmt(space.w_ValueError,
+                            "cannot convert signaling NaN to float")
+            if rmpdec.mpd_isnegative(self.mpd):
+                w_s = space.wrap("-nan")
+            else:
+                w_s = space.wrap("nan")
+        else:
+            w_s = self.descr_str(space)
+        return space.call_function(space.w_float, w_s)
+
     def compare(self, space, w_other, op):
         if not isinstance(w_other, W_Decimal):  # So far
             return space.w_NotImplemented
@@ -78,6 +92,12 @@
     def descr_eq(self, space, w_other):
         return self.compare(space, w_other, 'eq')
 
+    # Boolean functions
+    def is_qnan_w(self, space):
+        return space.wrap(bool(rmpdec.mpd_isqnan(self.mpd)))
+    def is_infinite_w(self, space):
+        return space.wrap(bool(rmpdec.mpd_isinfinite(self.mpd)))
+
 
 # Constructors
 def decimal_from_ssize(space, w_subtype, value, context, exact=True):
@@ -106,12 +126,10 @@
         s = s.strip()
     return decimal_from_cstring(space, w_subtype, s, context, exact=exact)
 
-def decimal_from_long(space, w_subtype, w_value, context, exact=True):
+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)
 
-    value = space.bigint_w(w_value)
-
     with interp_context.ConvContext(
             space, w_result.mpd, context, exact) as (ctx, status_ptr):
         if value.sign == -1:
@@ -233,7 +251,55 @@
             rmpdec.mpd_setspecial(w_result.mpd, rmpdec.MPD_POS, rmpdec.MPD_NAN)
         else:
             return w_value.apply(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)
+        # 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)
+        rmpdec.mpd_setspecial(w_result.mpd, sign, rmpdec.MPD_INF)
+        return w_result
+
+    # float as integer ratio: numerator/denominator
+    num, den = rfloat.float_as_rbigint_ratio(abs(value))
+    k = den.bit_length() - 1
+
+    w_result = decimal_from_bigint(space, w_subtype, num, context, exact=True)
+
+    # Compute num * 5**k
+    d1 = rmpdec.mpd_qnew()
+    if not d1:
+        raise OperationError(space.w_MemoryError, space.w_None)
+    d2 = rmpdec.mpd_qnew()
+    if not d2:
+        raise OperationError(space.w_MemoryError, space.w_None)
+    with interp_context.ConvContext(
+            space, w_result.mpd, context, exact=True) as (ctx, status_ptr):
+        rmpdec.mpd_qset_uint(d1, 5, ctx, status_ptr)
+        rmpdec.mpd_qset_ssize(d2, k, ctx, status_ptr)
+        rmpdec.mpd_qpow(d1, d1, d2, ctx, status_ptr)
+    with interp_context.ConvContext(
+            space, w_result.mpd, context, exact=True) as (ctx, status_ptr):
+        rmpdec.mpd_qmul(w_result.mpd, w_result.mpd, d1, ctx, status_ptr)
+
+    # result = +- n * 5**k * 10**-k
+    rmpdec.mpd_set_sign(w_result.mpd, sign)
+    w_result.mpd.c_exp = - k
+
+    if not exact:
+        with lltype.scoped_alloc(rffi.CArrayPtr(rffi.UINT).TO, 1) as 
status_ptr:
+            rmpdec.mpd_qfinalize(w_result.mpd, context.ctx, status_ptr)
+            context.addstatus(space, status_ptr[0])
+    return w_result
 
 def decimal_from_object(space, w_subtype, w_value, context, exact=True):
     if w_value is None:
@@ -245,12 +311,17 @@
         return decimal_from_unicode(space, w_subtype, w_value, context,
                                     exact=exact, strip_whitespace=exact)
     elif space.isinstance_w(w_value, space.w_int):
-        return decimal_from_long(space, w_subtype, w_value, context,
-                                 exact=exact)
+        value = space.bigint_w(w_value)
+        return decimal_from_bigint(space, w_subtype, value, context,
+                                   exact=exact)
     elif (space.isinstance_w(w_value, space.w_list) or
           space.isinstance_w(w_value, space.w_tuple)):
         return decimal_from_tuple(space, w_subtype, w_value, context,
                                  exact=exact)
+    elif space.isinstance_w(w_value, space.w_float):
+        context.addstatus(space, rmpdec.MPD_Float_operation)
+        return decimal_from_float(space, w_subtype, w_value, context,
+                                  exact=exact)
     raise oefmt(space.w_TypeError,
                 "conversion from %N to Decimal is not supported",
                 space.type(w_value))
@@ -266,5 +337,8 @@
     __new__ = interp2app(descr_new_decimal),
     __str__ = interp2app(W_Decimal.descr_str),
     __bool__ = interp2app(W_Decimal.descr_bool),
+    __float__ = interp2app(W_Decimal.descr_float),
     __eq__ = interp2app(W_Decimal.descr_eq),
+    is_qnan = interp2app(W_Decimal.is_qnan_w),
+    is_infinite = interp2app(W_Decimal.is_infinite_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
@@ -1,11 +1,18 @@
+from pypy.interpreter import gateway
+import random
+
 class AppTestExplicitConstruction:
-    spaceconfig = dict(usemodules=('_decimal',))
+    spaceconfig = dict(usemodules=('_decimal', '_random'))
 
     def setup_class(cls):
         space = cls.space
         cls.w_decimal = space.call_function(space.builtin.get('__import__'),
                                             space.wrap("_decimal"))
         cls.w_Decimal = space.getattr(cls.w_decimal, space.wrap("Decimal"))
+        def random_float(space):
+            f = random.expovariate(0.01) * (random.random() * 2.0 - 1.0)
+            return space.wrap(f)
+        cls.w_random_float = space.wrap(gateway.interp2app(random_float))
 
     def test_explicit_empty(self):
         Decimal = self.Decimal
@@ -179,3 +186,20 @@
         e = Decimal(d)
         assert str(e) == '0'
 
+    def test_explicit_from_float(self):
+        Decimal = self.decimal.Decimal
+
+        r = Decimal(0.1)
+        assert type(r) is Decimal
+        assert str(r) == (
+                '0.1000000000000000055511151231257827021181583404541015625')
+        assert Decimal(float('nan')).is_qnan()
+        assert Decimal(float('inf')).is_infinite()
+        assert Decimal(float('-inf')).is_infinite()
+        assert str(Decimal(float('nan'))) == str(Decimal('NaN'))
+        assert str(Decimal(float('inf'))) == str(Decimal('Infinity'))
+        assert str(Decimal(float('-inf'))) == str(Decimal('-Infinity'))
+        assert str(Decimal(float('-0.0'))) == str(Decimal('-0'))
+        for i in range(200):
+            x = self.random_float()
+            assert x == float(Decimal(x)) # roundtrip
diff --git a/rpython/rlib/rmpdec.py b/rpython/rlib/rmpdec.py
--- a/rpython/rlib/rmpdec.py
+++ b/rpython/rlib/rmpdec.py
@@ -12,7 +12,7 @@
 if sys.maxsize > 1<<32:
     compile_extra.append("-DCONFIG_64")
     # This suppose a x64 platform with gcc inline assembler.
-    compile_extra.append("-DASM")
+    compile_extra.append("-DANSI")
 else:
     compile_extra.append("-DCONFIG_32")
     compile_extra.append("-DANSI")
@@ -36,13 +36,17 @@
                            libdir.join('memory.c'),
                            ],
     export_symbols=[
-        "mpd_qset_ssize", "mpd_qset_string", "mpd_qcopy", "mpd_setspecial",
+        "mpd_qset_ssize", "mpd_qset_uint", "mpd_qset_string", "mpd_qcopy", 
"mpd_setspecial",
+        "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",
         "mpd_maxcontext",
+        "mpd_qnew",
         "mpd_to_sci_size",
-        "mpd_iszero", "mpd_isnan",
+        "mpd_iszero", "mpd_isnegative", "mpd_isinfinite",
+        "mpd_isnan", "mpd_issnan", "mpd_isqnan",
         "mpd_qcmp",
+        "mpd_qpow", "mpd_qmul",
         ],
     compile_extra=compile_extra,
     libraries=['m'],
@@ -64,6 +68,13 @@
 
 class CConfig:
     _compilation_info_ = eci
+    MPD_UINT_T = platform.SimpleType('mpd_uint_t', rffi.INT)
+
+MPD_UINT_T = platform.configure(CConfig)['MPD_UINT_T']
+MPD_UINT_PTR = rffi.CArrayPtr(MPD_UINT_T)
+
+class CConfig:
+    _compilation_info_ = eci
 
     MPD_IEEE_CONTEXT_MAX_BITS = platform.ConstantInteger(
         'MPD_IEEE_CONTEXT_MAX_BITS')
@@ -73,6 +84,7 @@
     MPD_POS = platform.ConstantInteger('MPD_POS')
     MPD_NEG = platform.ConstantInteger('MPD_NEG')
     MPD_NAN = platform.ConstantInteger('MPD_NAN')
+    MPD_INF = platform.ConstantInteger('MPD_INF')
     MPD_STATIC = platform.ConstantInteger('MPD_STATIC')
     MPD_STATIC_DATA = platform.ConstantInteger('MPD_STATIC_DATA')
 
@@ -84,12 +96,12 @@
         locals()[name] = platform.ConstantInteger(name)
 
     MPD_T = platform.Struct('mpd_t',
-                            [('flags', rffi.UINT),
+                            [('flags', rffi.UCHAR),
                              ('exp', rffi.SSIZE_T),
                              ('digits', rffi.SSIZE_T),
                              ('len', rffi.SSIZE_T),
                              ('alloc', rffi.SSIZE_T),
-                             ('data', rffi.UINTP),
+                             ('data', MPD_UINT_PTR),
                              ])
     MPD_CONTEXT_T = platform.Struct('mpd_context_t',
                                     [('traps', rffi.UINT),
@@ -110,6 +122,8 @@
 # Initialization
 mpd_qset_ssize = external(
     'mpd_qset_ssize', [MPD_PTR, rffi.SSIZE_T, MPD_CONTEXT_PTR, rffi.UINTP], 
lltype.Void)
+mpd_qset_uint = external(
+    'mpd_qset_uint', [MPD_PTR, rffi.UINT, MPD_CONTEXT_PTR, rffi.UINTP], 
lltype.Void)
 mpd_qset_string = external(
     'mpd_qset_string', [MPD_PTR, rffi.CCHARP, MPD_CONTEXT_PTR, rffi.UINTP], 
lltype.Void)
 mpd_qimport_u32 = external(
@@ -119,6 +133,10 @@
     'mpd_qcopy', [MPD_PTR, MPD_PTR, rffi.UINTP], rffi.INT)
 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_qfinalize = external(
+    'mpd_qfinalize', [MPD_PTR, MPD_CONTEXT_PTR, rffi.UINTP], lltype.Void)
 
 # Context operations
 mpd_getprec = external(
@@ -146,6 +164,8 @@
 mpd_maxcontext = external(
     'mpd_maxcontext', [MPD_CONTEXT_PTR], lltype.Void)
 
+mpd_qnew = external(
+    'mpd_qnew', [], MPD_PTR)
 mpd_free = external(
     'mpd_free', [rffi.VOIDP], lltype.Void, macro=True)
 
@@ -159,7 +179,24 @@
 # Operations
 mpd_iszero = external(
     'mpd_iszero', [MPD_PTR], rffi.INT)
+mpd_isnegative = external(
+    'mpd_isnegative', [MPD_PTR], rffi.INT)
+mpd_isinfinite = external(
+    'mpd_isinfinite', [MPD_PTR], rffi.INT)
 mpd_isnan = external(
     'mpd_isnan', [MPD_PTR], rffi.INT)
+mpd_issnan = external(
+    'mpd_issnan', [MPD_PTR], rffi.INT)
+mpd_isqnan = external(
+    'mpd_isqnan', [MPD_PTR], rffi.INT)
 mpd_qcmp = external(
     'mpd_qcmp', [MPD_PTR, MPD_PTR, rffi.UINTP], rffi.INT)
+
+mpd_qpow = external(
+    'mpd_qpow',
+    [MPD_PTR, MPD_PTR, MPD_PTR, MPD_CONTEXT_PTR, rffi.UINTP],
+    lltype.Void)
+mpd_qmul = external(
+    'mpd_qmul',
+    [MPD_PTR, MPD_PTR, MPD_PTR, MPD_CONTEXT_PTR, rffi.UINTP],
+    lltype.Void)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to