[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