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