Author: Amaury Forgeot d'Arc <amaur...@gmail.com> 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 pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit