Author: Amaury Forgeot d'Arc <[email protected]>
Branch: decimal-libmpdec
Changeset: r71455:c80a6ebb000b
Date: 2014-05-06 22:00 +0200
http://bitbucket.org/pypy/pypy/changeset/c80a6ebb000b/
Log: Add tuple->Decimal conversion
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,5 @@
from rpython.rlib import rmpdec, rarithmetic, rbigint
+from rpython.rlib.rstring import StringBuilder
from rpython.rtyper.lltypesystem import rffi, lltype
from pypy.interpreter.baseobjspace import W_Root
from pypy.interpreter.error import oefmt, OperationError
@@ -118,6 +119,82 @@
raise ValueError("Bad rbigint size")
return w_result
+def decimal_from_tuple(space, w_subtype, w_value, context, exact=True):
+ w_sign, w_digits, w_exponent = space.unpackiterable(w_value, 3)
+
+ # Make a string representation of a DecimalTuple
+ builder = StringBuilder(20)
+
+ # sign
+ try:
+ sign = space.int_w(w_sign)
+ except OperationError as e:
+ if not e.match(space, space.w_TypeError):
+ raise
+ sign = -1
+ if sign != 0 and sign != 1:
+ raise oefmt(space.w_ValueError,
+ "sign must be an integer with the value 0 or 1")
+ builder.append('-' if sign else '+')
+
+ # exponent or encoding for a special number
+ is_infinite = False
+ is_special = False
+ exponent = 0
+ if space.isinstance_w(w_exponent, space.w_unicode):
+ # special
+ is_special = True
+ val = space.unicode_w(w_exponent)
+ if val == 'F':
+ builder.append('Inf')
+ is_infinite = True
+ elif val == 'n':
+ builder.append('Nan')
+ elif val == 'N':
+ builder.append('sNan')
+ else:
+ raise oefmt(space.w_ValueError,
+ "string argument in the third position "
+ "must be 'F', 'n' or 'N'")
+ else:
+ # exponent
+ try:
+ exponent = space.int_w(w_exponent)
+ except OperationError as e:
+ if not e.match(space, space.w_TypeError):
+ raise
+ raise oefmt(space.w_ValueError,
+ "exponent must be an integer")
+
+ # coefficient
+ digits_w = space.unpackiterable(w_digits)
+
+ if not digits_w and not is_special:
+ # empty tuple: zero coefficient, except for special numbers
+ strval += '0'
+ for w_digit in digits_w:
+ try:
+ digit = space.int_w(w_digit)
+ except OperationError as e:
+ if not e.match(space, space.w_TypeError):
+ raise
+ digit = -1
+ if not 0 <= digit <= 9:
+ raise oefmt(space.w_ValueError,
+ "coefficient must be a tuple of digits")
+ if is_infinite:
+ # accept but ignore any well-formed coefficient for
+ # compatibility with decimal.py
+ continue
+ builder.append(chr(ord('0') + digit))
+
+ if not is_special:
+ builder.append('E')
+ builder.append(str(exponent))
+
+ strval = builder.build()
+ return decimal_from_cstring(space, w_subtype, strval, context, exact=exact)
+
def decimal_from_object(space, w_subtype, w_value, context, exact=True):
if w_value is None:
return decimal_from_ssize(space, w_subtype, 0, context, exact=exact)
@@ -127,6 +204,10 @@
elif space.isinstance_w(w_value, space.w_int):
return decimal_from_long(space, w_subtype, w_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)
raise oefmt(space.w_TypeError,
"conversion from %N to Decimal is not supported",
space.type(w_value))
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
@@ -90,3 +90,61 @@
# embedded NUL
raises(InvalidOperation, Decimal, "12\u00003")
+ def test_explicit_from_tuples(self):
+ Decimal = self.decimal.Decimal
+
+ #zero
+ d = Decimal( (0, (0,), 0) )
+ assert str(d) == '0'
+
+ #int
+ d = Decimal( (1, (4, 5), 0) )
+ assert str(d) == '-45'
+
+ #float
+ d = Decimal( (0, (4, 5, 3, 4), -2) )
+ assert str(d) == '45.34'
+
+ #weird
+ d = Decimal( (1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25) )
+ assert str(d) == '-4.34913534E-17'
+
+ #inf
+ d = Decimal( (0, (), "F") )
+ assert str(d) == 'Infinity'
+
+ #wrong number of items
+ raises(ValueError, Decimal, (1, (4, 3, 4, 9, 1)) )
+
+ #bad sign
+ raises(ValueError, Decimal, (8, (4, 3, 4, 9, 1), 2) )
+ raises(ValueError, Decimal, (0., (4, 3, 4, 9, 1), 2) )
+ raises(ValueError, Decimal, (Decimal(1), (4, 3, 4, 9, 1), 2))
+
+ #bad exp
+ raises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 'wrong!') )
+ raises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), 0.) )
+ raises(ValueError, Decimal, (1, (4, 3, 4, 9, 1), '1') )
+
+ #bad coefficients
+ raises(ValueError, Decimal, (1, "xyz", 2) )
+ raises(ValueError, Decimal, (1, (4, 3, 4, None, 1), 2) )
+ raises(ValueError, Decimal, (1, (4, -3, 4, 9, 1), 2) )
+ raises(ValueError, Decimal, (1, (4, 10, 4, 9, 1), 2) )
+ raises(ValueError, Decimal, (1, (4, 3, 4, 'a', 1), 2) )
+
+ def test_explicit_from_list(self):
+ Decimal = self.decimal.Decimal
+
+ d = Decimal([0, [0], 0])
+ assert str(d) == '0'
+
+ d = Decimal([1, [4, 3, 4, 9, 1, 3, 5, 3, 4], -25])
+ assert str(d) == '-4.34913534E-17'
+
+ d = Decimal([1, (4, 3, 4, 9, 1, 3, 5, 3, 4), -25])
+ assert str(d) == '-4.34913534E-17'
+
+ d = Decimal((1, [4, 3, 4, 9, 1, 3, 5, 3, 4], -25))
+ assert str(d) == '-4.34913534E-17'
+
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit