diff options
-rw-r--r-- | Doc/library/decimal.rst | 7 | ||||
-rw-r--r-- | Lib/decimal.py | 360 | ||||
-rw-r--r-- | Lib/test/test_decimal.py | 438 | ||||
-rw-r--r-- | Misc/ACKS | 1 | ||||
-rw-r--r-- | Misc/NEWS | 6 |
5 files changed, 792 insertions, 20 deletions
diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index c1c8482..a3325f7 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -896,8 +896,11 @@ In addition to the three supplied contexts, new contexts can be created with the a large number of methods for doing arithmetic directly in a given context. In addition, for each of the :class:`Decimal` methods described above (with the exception of the :meth:`adjusted` and :meth:`as_tuple` methods) there is - a corresponding :class:`Context` method. For example, ``C.exp(x)`` is - equivalent to ``x.exp(context=C)``. + a corresponding :class:`Context` method. For example, for a :class:`Context` + instance ``C`` and :class:`Decimal` instance ``x``, ``C.exp(x)`` is + equivalent to ``x.exp(context=C)``. Each :class:`Context` method accepts a + Python integer (an instance of :class:`int` or :class:`long`) anywhere that a + Decimal instance is accepted. .. method:: clear_flags() diff --git a/Lib/decimal.py b/Lib/decimal.py index d0c4df2..370eb82 100644 --- a/Lib/decimal.py +++ b/Lib/decimal.py @@ -2896,6 +2896,7 @@ class Decimal(object): def copy_sign(self, other): """Returns self with the sign of other.""" + other = _convert_other(other, raiseit=True) return _dec_from_triple(other._sign, self._int, self._exp, self._is_special) @@ -3933,7 +3934,10 @@ class Context(object): Decimal('101.5') >>> ExtendedContext.abs(Decimal('-101.5')) Decimal('101.5') + >>> ExtendedContext.abs(-1) + Decimal('1') """ + a = _convert_other(a, raiseit=True) return a.__abs__(context=self) def add(self, a, b): @@ -3943,8 +3947,19 @@ class Context(object): Decimal('19.00') >>> ExtendedContext.add(Decimal('1E+2'), Decimal('1.01E+4')) Decimal('1.02E+4') + >>> ExtendedContext.add(1, Decimal(2)) + Decimal('3') + >>> ExtendedContext.add(Decimal(8), 5) + Decimal('13') + >>> ExtendedContext.add(5, 5) + Decimal('10') """ - return a.__add__(b, context=self) + a = _convert_other(a, raiseit=True) + r = a.__add__(b, context=self) + if r is NotImplemented: + raise TypeError("Unable to convert %s to Decimal" % b) + else: + return r def _apply(self, a): return str(a._fix(self)) @@ -3986,7 +4001,14 @@ class Context(object): Decimal('1') >>> ExtendedContext.compare(Decimal('-3'), Decimal('2.1')) Decimal('-1') + >>> ExtendedContext.compare(1, 2) + Decimal('-1') + >>> ExtendedContext.compare(Decimal(1), 2) + Decimal('-1') + >>> ExtendedContext.compare(1, Decimal(2)) + Decimal('-1') """ + a = _convert_other(a, raiseit=True) return a.compare(b, context=self) def compare_signal(self, a, b): @@ -4014,7 +4036,14 @@ class Context(object): Decimal('NaN') >>> print(c.flags[InvalidOperation]) 1 + >>> c.compare_signal(-1, 2) + Decimal('-1') + >>> c.compare_signal(Decimal(-1), 2) + Decimal('-1') + >>> c.compare_signal(-1, Decimal(2)) + Decimal('-1') """ + a = _convert_other(a, raiseit=True) return a.compare_signal(b, context=self) def compare_total(self, a, b): @@ -4036,7 +4065,14 @@ class Context(object): Decimal('1') >>> ExtendedContext.compare_total(Decimal('12.3'), Decimal('NaN')) Decimal('-1') + >>> ExtendedContext.compare_total(1, 2) + Decimal('-1') + >>> ExtendedContext.compare_total(Decimal(1), 2) + Decimal('-1') + >>> ExtendedContext.compare_total(1, Decimal(2)) + Decimal('-1') """ + a = _convert_other(a, raiseit=True) return a.compare_total(b) def compare_total_mag(self, a, b): @@ -4044,6 +4080,7 @@ class Context(object): Like compare_total, but with operand's sign ignored and assumed to be 0. """ + a = _convert_other(a, raiseit=True) return a.compare_total_mag(b) def copy_abs(self, a): @@ -4053,17 +4090,23 @@ class Context(object): Decimal('2.1') >>> ExtendedContext.copy_abs(Decimal('-100')) Decimal('100') + >>> ExtendedContext.copy_abs(-1) + Decimal('1') """ + a = _convert_other(a, raiseit=True) return a.copy_abs() def copy_decimal(self, a): - """Returns a copy of the decimal objet. + """Returns a copy of the decimal object. >>> ExtendedContext.copy_decimal(Decimal('2.1')) Decimal('2.1') >>> ExtendedContext.copy_decimal(Decimal('-1.00')) Decimal('-1.00') + >>> ExtendedContext.copy_decimal(1) + Decimal('1') """ + a = _convert_other(a, raiseit=True) return Decimal(a) def copy_negate(self, a): @@ -4073,7 +4116,10 @@ class Context(object): Decimal('-101.5') >>> ExtendedContext.copy_negate(Decimal('-101.5')) Decimal('101.5') + >>> ExtendedContext.copy_negate(1) + Decimal('-1') """ + a = _convert_other(a, raiseit=True) return a.copy_negate() def copy_sign(self, a, b): @@ -4090,7 +4136,14 @@ class Context(object): Decimal('-1.50') >>> ExtendedContext.copy_sign(Decimal('-1.50'), Decimal('-7.33')) Decimal('-1.50') + >>> ExtendedContext.copy_sign(1, -2) + Decimal('-1') + >>> ExtendedContext.copy_sign(Decimal(1), -2) + Decimal('-1') + >>> ExtendedContext.copy_sign(1, Decimal(-2)) + Decimal('-1') """ + a = _convert_other(a, raiseit=True) return a.copy_sign(b) def divide(self, a, b): @@ -4116,8 +4169,19 @@ class Context(object): Decimal('1000') >>> ExtendedContext.divide(Decimal('2.40E+6'), Decimal('2')) Decimal('1.20E+6') + >>> ExtendedContext.divide(5, 5) + Decimal('1') + >>> ExtendedContext.divide(Decimal(5), 5) + Decimal('1') + >>> ExtendedContext.divide(5, Decimal(5)) + Decimal('1') """ - return a.__truediv__(b, context=self) + a = _convert_other(a, raiseit=True) + r = a.__truediv__(b, context=self) + if r is NotImplemented: + raise TypeError("Unable to convert %s to Decimal" % b) + else: + return r def divide_int(self, a, b): """Divides two numbers and returns the integer part of the result. @@ -4128,18 +4192,40 @@ class Context(object): Decimal('3') >>> ExtendedContext.divide_int(Decimal('1'), Decimal('0.3')) Decimal('3') + >>> ExtendedContext.divide_int(10, 3) + Decimal('3') + >>> ExtendedContext.divide_int(Decimal(10), 3) + Decimal('3') + >>> ExtendedContext.divide_int(10, Decimal(3)) + Decimal('3') """ - return a.__floordiv__(b, context=self) + a = _convert_other(a, raiseit=True) + r = a.__floordiv__(b, context=self) + if r is NotImplemented: + raise TypeError("Unable to convert %s to Decimal" % b) + else: + return r def divmod(self, a, b): - """Return (a // b, a % b) + """Return (a // b, a % b). >>> ExtendedContext.divmod(Decimal(8), Decimal(3)) (Decimal('2'), Decimal('2')) >>> ExtendedContext.divmod(Decimal(8), Decimal(4)) (Decimal('2'), Decimal('0')) + >>> ExtendedContext.divmod(8, 4) + (Decimal('2'), Decimal('0')) + >>> ExtendedContext.divmod(Decimal(8), 4) + (Decimal('2'), Decimal('0')) + >>> ExtendedContext.divmod(8, Decimal(4)) + (Decimal('2'), Decimal('0')) """ - return a.__divmod__(b, context=self) + a = _convert_other(a, raiseit=True) + r = a.__divmod__(b, context=self) + if r is NotImplemented: + raise TypeError("Unable to convert %s to Decimal" % b) + else: + return r def exp(self, a): """Returns e ** a. @@ -4159,7 +4245,10 @@ class Context(object): Decimal('2.00000000') >>> c.exp(Decimal('+Infinity')) Decimal('Infinity') + >>> c.exp(10) + Decimal('22026.4658') """ + a =_convert_other(a, raiseit=True) return a.exp(context=self) def fma(self, a, b, c): @@ -4175,7 +4264,14 @@ class Context(object): Decimal('-8') >>> ExtendedContext.fma(Decimal('888565290'), Decimal('1557.96930'), Decimal('-86087.7578')) Decimal('1.38435736E+12') + >>> ExtendedContext.fma(1, 3, 4) + Decimal('7') + >>> ExtendedContext.fma(1, Decimal(3), 4) + Decimal('7') + >>> ExtendedContext.fma(1, 3, Decimal(4)) + Decimal('7') """ + a = _convert_other(a, raiseit=True) return a.fma(b, c, context=self) def is_canonical(self, a): @@ -4205,7 +4301,10 @@ class Context(object): False >>> ExtendedContext.is_finite(Decimal('NaN')) False + >>> ExtendedContext.is_finite(1) + True """ + a = _convert_other(a, raiseit=True) return a.is_finite() def is_infinite(self, a): @@ -4217,7 +4316,10 @@ class Context(object): True >>> ExtendedContext.is_infinite(Decimal('NaN')) False + >>> ExtendedContext.is_infinite(1) + False """ + a = _convert_other(a, raiseit=True) return a.is_infinite() def is_nan(self, a): @@ -4230,7 +4332,10 @@ class Context(object): True >>> ExtendedContext.is_nan(Decimal('-sNaN')) True + >>> ExtendedContext.is_nan(1) + False """ + a = _convert_other(a, raiseit=True) return a.is_nan() def is_normal(self, a): @@ -4250,7 +4355,10 @@ class Context(object): False >>> c.is_normal(Decimal('NaN')) False + >>> c.is_normal(1) + True """ + a = _convert_other(a, raiseit=True) return a.is_normal(context=self) def is_qnan(self, a): @@ -4262,7 +4370,10 @@ class Context(object): True >>> ExtendedContext.is_qnan(Decimal('sNaN')) False + >>> ExtendedContext.is_qnan(1) + False """ + a = _convert_other(a, raiseit=True) return a.is_qnan() def is_signed(self, a): @@ -4274,7 +4385,12 @@ class Context(object): True >>> ExtendedContext.is_signed(Decimal('-0')) True + >>> ExtendedContext.is_signed(8) + False + >>> ExtendedContext.is_signed(-8) + True """ + a = _convert_other(a, raiseit=True) return a.is_signed() def is_snan(self, a): @@ -4287,7 +4403,10 @@ class Context(object): False >>> ExtendedContext.is_snan(Decimal('sNaN')) True + >>> ExtendedContext.is_snan(1) + False """ + a = _convert_other(a, raiseit=True) return a.is_snan() def is_subnormal(self, a): @@ -4306,7 +4425,10 @@ class Context(object): False >>> c.is_subnormal(Decimal('NaN')) False + >>> c.is_subnormal(1) + False """ + a = _convert_other(a, raiseit=True) return a.is_subnormal(context=self) def is_zero(self, a): @@ -4318,7 +4440,12 @@ class Context(object): False >>> ExtendedContext.is_zero(Decimal('-0E+2')) True + >>> ExtendedContext.is_zero(1) + False + >>> ExtendedContext.is_zero(0) + True """ + a = _convert_other(a, raiseit=True) return a.is_zero() def ln(self, a): @@ -4337,7 +4464,10 @@ class Context(object): Decimal('2.30258509') >>> c.ln(Decimal('+Infinity')) Decimal('Infinity') + >>> c.ln(1) + Decimal('0') """ + a = _convert_other(a, raiseit=True) return a.ln(context=self) def log10(self, a): @@ -4360,7 +4490,12 @@ class Context(object): Decimal('1.84509804') >>> c.log10(Decimal('+Infinity')) Decimal('Infinity') + >>> c.log10(0) + Decimal('-Infinity') + >>> c.log10(1) + Decimal('0') """ + a = _convert_other(a, raiseit=True) return a.log10(context=self) def logb(self, a): @@ -4379,7 +4514,14 @@ class Context(object): Decimal('-2') >>> ExtendedContext.logb(Decimal('0')) Decimal('-Infinity') + >>> ExtendedContext.logb(1) + Decimal('0') + >>> ExtendedContext.logb(10) + Decimal('1') + >>> ExtendedContext.logb(100) + Decimal('2') """ + a = _convert_other(a, raiseit=True) return a.logb(context=self) def logical_and(self, a, b): @@ -4399,7 +4541,14 @@ class Context(object): Decimal('1000') >>> ExtendedContext.logical_and(Decimal('1111'), Decimal('10')) Decimal('10') + >>> ExtendedContext.logical_and(110, 1101) + Decimal('100') + >>> ExtendedContext.logical_and(Decimal(110), 1101) + Decimal('100') + >>> ExtendedContext.logical_and(110, Decimal(1101)) + Decimal('100') """ + a = _convert_other(a, raiseit=True) return a.logical_and(b, context=self) def logical_invert(self, a): @@ -4415,7 +4564,10 @@ class Context(object): Decimal('0') >>> ExtendedContext.logical_invert(Decimal('101010101')) Decimal('10101010') + >>> ExtendedContext.logical_invert(1101) + Decimal('111110010') """ + a = _convert_other(a, raiseit=True) return a.logical_invert(context=self) def logical_or(self, a, b): @@ -4435,7 +4587,14 @@ class Context(object): Decimal('1110') >>> ExtendedContext.logical_or(Decimal('1110'), Decimal('10')) Decimal('1110') + >>> ExtendedContext.logical_or(110, 1101) + Decimal('1111') + >>> ExtendedContext.logical_or(Decimal(110), 1101) + Decimal('1111') + >>> ExtendedContext.logical_or(110, Decimal(1101)) + Decimal('1111') """ + a = _convert_other(a, raiseit=True) return a.logical_or(b, context=self) def logical_xor(self, a, b): @@ -4455,10 +4614,17 @@ class Context(object): Decimal('110') >>> ExtendedContext.logical_xor(Decimal('1111'), Decimal('10')) Decimal('1101') + >>> ExtendedContext.logical_xor(110, 1101) + Decimal('1011') + >>> ExtendedContext.logical_xor(Decimal(110), 1101) + Decimal('1011') + >>> ExtendedContext.logical_xor(110, Decimal(1101)) + Decimal('1011') """ + a = _convert_other(a, raiseit=True) return a.logical_xor(b, context=self) - def max(self, a,b): + def max(self, a, b): """max compares two values numerically and returns the maximum. If either operand is a NaN then the general rules apply. @@ -4475,14 +4641,34 @@ class Context(object): Decimal('1') >>> ExtendedContext.max(Decimal('7'), Decimal('NaN')) Decimal('7') + >>> ExtendedContext.max(1, 2) + Decimal('2') + >>> ExtendedContext.max(Decimal(1), 2) + Decimal('2') + >>> ExtendedContext.max(1, Decimal(2)) + Decimal('2') """ + a = _convert_other(a, raiseit=True) return a.max(b, context=self) def max_mag(self, a, b): - """Compares the values numerically with their sign ignored.""" + """Compares the values numerically with their sign ignored. + + >>> ExtendedContext.max_mag(Decimal('7'), Decimal('NaN')) + Decimal('7') + >>> ExtendedContext.max_mag(Decimal('7'), Decimal('-10')) + Decimal('-10') + >>> ExtendedContext.max_mag(1, -2) + Decimal('-2') + >>> ExtendedContext.max_mag(Decimal(1), -2) + Decimal('-2') + >>> ExtendedContext.max_mag(1, Decimal(-2)) + Decimal('-2') + """ + a = _convert_other(a, raiseit=True) return a.max_mag(b, context=self) - def min(self, a,b): + def min(self, a, b): """min compares two values numerically and returns the minimum. If either operand is a NaN then the general rules apply. @@ -4499,11 +4685,31 @@ class Context(object): Decimal('1.0') >>> ExtendedContext.min(Decimal('7'), Decimal('NaN')) Decimal('7') + >>> ExtendedContext.min(1, 2) + Decimal('1') + >>> ExtendedContext.min(Decimal(1), 2) + Decimal('1') + >>> ExtendedContext.min(1, Decimal(29)) + Decimal('1') """ + a = _convert_other(a, raiseit=True) return a.min(b, context=self) def min_mag(self, a, b): - """Compares the values numerically with their sign ignored.""" + """Compares the values numerically with their sign ignored. + + >>> ExtendedContext.min_mag(Decimal('3'), Decimal('-2')) + Decimal('-2') + >>> ExtendedContext.min_mag(Decimal('-3'), Decimal('NaN')) + Decimal('-3') + >>> ExtendedContext.min_mag(1, -2) + Decimal('1') + >>> ExtendedContext.min_mag(Decimal(1), -2) + Decimal('1') + >>> ExtendedContext.min_mag(1, Decimal(-2)) + Decimal('1') + """ + a = _convert_other(a, raiseit=True) return a.min_mag(b, context=self) def minus(self, a): @@ -4517,16 +4723,19 @@ class Context(object): Decimal('-1.3') >>> ExtendedContext.minus(Decimal('-1.3')) Decimal('1.3') + >>> ExtendedContext.minus(1) + Decimal('-1') """ + a = _convert_other(a, raiseit=True) return a.__neg__(context=self) def multiply(self, a, b): """multiply multiplies two operands. If either operand is a special value then the general rules apply. - Otherwise, the operands are multiplied together ('long multiplication'), - resulting in a number which may be as long as the sum of the lengths - of the two operands. + Otherwise, the operands are multiplied together + ('long multiplication'), resulting in a number which may be as long as + the sum of the lengths of the two operands. >>> ExtendedContext.multiply(Decimal('1.20'), Decimal('3')) Decimal('3.60') @@ -4538,8 +4747,19 @@ class Context(object): Decimal('-0.0') >>> ExtendedContext.multiply(Decimal('654321'), Decimal('654321')) Decimal('4.28135971E+11') + >>> ExtendedContext.multiply(7, 7) + Decimal('49') + >>> ExtendedContext.multiply(Decimal(7), 7) + Decimal('49') + >>> ExtendedContext.multiply(7, Decimal(7)) + Decimal('49') """ - return a.__mul__(b, context=self) + a = _convert_other(a, raiseit=True) + r = a.__mul__(b, context=self) + if r is NotImplemented: + raise TypeError("Unable to convert %s to Decimal" % b) + else: + return r def next_minus(self, a): """Returns the largest representable number smaller than a. @@ -4555,7 +4775,10 @@ class Context(object): Decimal('-1.00000004') >>> c.next_minus(Decimal('Infinity')) Decimal('9.99999999E+999') + >>> c.next_minus(1) + Decimal('0.999999999') """ + a = _convert_other(a, raiseit=True) return a.next_minus(context=self) def next_plus(self, a): @@ -4572,7 +4795,10 @@ class Context(object): Decimal('-1.00000002') >>> c.next_plus(Decimal('-Infinity')) Decimal('-9.99999999E+999') + >>> c.next_plus(1) + Decimal('1.00000001') """ + a = _convert_other(a, raiseit=True) return a.next_plus(context=self) def next_toward(self, a, b): @@ -4600,7 +4826,14 @@ class Context(object): Decimal('-1.00000004') >>> c.next_toward(Decimal('0.00'), Decimal('-0.0000')) Decimal('-0.00') + >>> c.next_toward(0, 1) + Decimal('1E-1007') + >>> c.next_toward(Decimal(0), 1) + Decimal('1E-1007') + >>> c.next_toward(0, Decimal(1)) + Decimal('1E-1007') """ + a = _convert_other(a, raiseit=True) return a.next_toward(b, context=self) def normalize(self, a): @@ -4621,7 +4854,10 @@ class Context(object): Decimal('1.2E+2') >>> ExtendedContext.normalize(Decimal('0.00')) Decimal('0') + >>> ExtendedContext.normalize(6) + Decimal('6') """ + a = _convert_other(a, raiseit=True) return a.normalize(context=self) def number_class(self, a): @@ -4668,7 +4904,10 @@ class Context(object): 'NaN' >>> c.number_class(Decimal('sNaN')) 'sNaN' + >>> c.number_class(123) + '+Normal' """ + a = _convert_other(a, raiseit=True) return a.number_class(context=self) def plus(self, a): @@ -4682,7 +4921,10 @@ class Context(object): Decimal('1.3') >>> ExtendedContext.plus(Decimal('-1.3')) Decimal('-1.3') + >>> ExtendedContext.plus(-1) + Decimal('-1') """ + a = _convert_other(a, raiseit=True) return a.__pos__(context=self) def power(self, a, b, modulo=None): @@ -4751,8 +4993,19 @@ class Context(object): Decimal('-0') >>> c.power(Decimal('-23'), Decimal('0'), Decimal('65537')) Decimal('1') + >>> ExtendedContext.power(7, 7) + Decimal('823543') + >>> ExtendedContext.power(Decimal(7), 7) + Decimal('823543') + >>> ExtendedContext.power(7, Decimal(7), 2) + Decimal('1') """ - return a.__pow__(b, modulo, context=self) + a = _convert_other(a, raiseit=True) + r = a.__pow__(b, modulo, context=self) + if r is NotImplemented: + raise TypeError("Unable to convert %s to Decimal" % b) + else: + return r def quantize(self, a, b): """Returns a value equal to 'a' (rounded), having the exponent of 'b'. @@ -4802,7 +5055,14 @@ class Context(object): Decimal('2.2E+2') >>> ExtendedContext.quantize(Decimal('217'), Decimal('1e+2')) Decimal('2E+2') + >>> ExtendedContext.quantize(1, 2) + Decimal('1') + >>> ExtendedContext.quantize(Decimal(1), 2) + Decimal('1') + >>> ExtendedContext.quantize(1, Decimal(2)) + Decimal('1') """ + a = _convert_other(a, raiseit=True) return a.quantize(b, context=self) def radix(self): @@ -4837,8 +5097,19 @@ class Context(object): Decimal('0.1') >>> ExtendedContext.remainder(Decimal('3.6'), Decimal('1.3')) Decimal('1.0') + >>> ExtendedContext.remainder(22, 6) + Decimal('4') + >>> ExtendedContext.remainder(Decimal(22), 6) + Decimal('4') + >>> ExtendedContext.remainder(22, Decimal(6)) + Decimal('4') """ - return a.__mod__(b, context=self) + a = _convert_other(a, raiseit=True) + r = a.__mod__(b, context=self) + if r is NotImplemented: + raise TypeError("Unable to convert %s to Decimal" % b) + else: + return r def remainder_near(self, a, b): """Returns to be "a - b * n", where n is the integer nearest the exact @@ -4864,7 +5135,14 @@ class Context(object): Decimal('0.1') >>> ExtendedContext.remainder_near(Decimal('3.6'), Decimal('1.3')) Decimal('-0.3') + >>> ExtendedContext.remainder_near(3, 11) + Decimal('3') + >>> ExtendedContext.remainder_near(Decimal(3), 11) + Decimal('3') + >>> ExtendedContext.remainder_near(3, Decimal(11)) + Decimal('3') """ + a = _convert_other(a, raiseit=True) return a.remainder_near(b, context=self) def rotate(self, a, b): @@ -4886,7 +5164,14 @@ class Context(object): Decimal('123456789') >>> ExtendedContext.rotate(Decimal('123456789'), Decimal('+2')) Decimal('345678912') + >>> ExtendedContext.rotate(1333333, 1) + Decimal('13333330') + >>> ExtendedContext.rotate(Decimal(1333333), 1) + Decimal('13333330') + >>> ExtendedContext.rotate(1333333, Decimal(1)) + Decimal('13333330') """ + a = _convert_other(a, raiseit=True) return a.rotate(b, context=self) def same_quantum(self, a, b): @@ -4903,7 +5188,14 @@ class Context(object): False >>> ExtendedContext.same_quantum(Decimal('Inf'), Decimal('-Inf')) True + >>> ExtendedContext.same_quantum(10000, -1) + True + >>> ExtendedContext.same_quantum(Decimal(10000), -1) + True + >>> ExtendedContext.same_quantum(10000, Decimal(-1)) + True """ + a = _convert_other(a, raiseit=True) return a.same_quantum(b) def scaleb (self, a, b): @@ -4915,8 +5207,15 @@ class Context(object): Decimal('7.50') >>> ExtendedContext.scaleb(Decimal('7.50'), Decimal('3')) Decimal('7.50E+3') + >>> ExtendedContext.scaleb(1, 4) + Decimal('1E+4') + >>> ExtendedContext.scaleb(Decimal(1), 4) + Decimal('1E+4') + >>> ExtendedContext.scaleb(1, Decimal(4)) + Decimal('1E+4') """ - return a.scaleb (b, context=self) + a = _convert_other(a, raiseit=True) + return a.scaleb(b, context=self) def shift(self, a, b): """Returns a shifted copy of a, b times. @@ -4938,7 +5237,14 @@ class Context(object): Decimal('123456789') >>> ExtendedContext.shift(Decimal('123456789'), Decimal('+2')) Decimal('345678900') + >>> ExtendedContext.shift(88888888, 2) + Decimal('888888800') + >>> ExtendedContext.shift(Decimal(88888888), 2) + Decimal('888888800') + >>> ExtendedContext.shift(88888888, Decimal(2)) + Decimal('888888800') """ + a = _convert_other(a, raiseit=True) return a.shift(b, context=self) def sqrt(self, a): @@ -4965,9 +5271,12 @@ class Context(object): Decimal('2.64575131') >>> ExtendedContext.sqrt(Decimal('10')) Decimal('3.16227766') + >>> ExtendedContext.sqrt(2) + Decimal('1.41421356') >>> ExtendedContext.prec 9 """ + a = _convert_other(a, raiseit=True) return a.sqrt(context=self) def subtract(self, a, b): @@ -4979,14 +5288,26 @@ class Context(object): Decimal('0.00') >>> ExtendedContext.subtract(Decimal('1.3'), Decimal('2.07')) Decimal('-0.77') + >>> ExtendedContext.subtract(8, 5) + Decimal('3') + >>> ExtendedContext.subtract(Decimal(8), 5) + Decimal('3') + >>> ExtendedContext.subtract(8, Decimal(5)) + Decimal('3') """ - return a.__sub__(b, context=self) + a = _convert_other(a, raiseit=True) + r = a.__sub__(b, context=self) + if r is NotImplemented: + raise TypeError("Unable to convert %s to Decimal" % b) + else: + return r def to_eng_string(self, a): """Converts a number to a string, using scientific notation. The operation is not affected by the context. """ + a = _convert_other(a, raiseit=True) return a.to_eng_string(context=self) def to_sci_string(self, a): @@ -4994,6 +5315,7 @@ class Context(object): The operation is not affected by the context. """ + a = _convert_other(a, raiseit=True) return a.__str__(context=self) def to_integral_exact(self, a): @@ -5023,6 +5345,7 @@ class Context(object): >>> ExtendedContext.to_integral_exact(Decimal('-Inf')) Decimal('-Infinity') """ + a = _convert_other(a, raiseit=True) return a.to_integral_exact(context=self) def to_integral_value(self, a): @@ -5051,6 +5374,7 @@ class Context(object): >>> ExtendedContext.to_integral_value(Decimal('-Inf')) Decimal('-Infinity') """ + a = _convert_other(a, raiseit=True) return a.to_integral_value(context=self) # the method name changed, but we provide also the old one, for compatibility diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index 42fc519..d303598 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -1078,6 +1078,12 @@ class DecimalArithmeticOperatorsTest(unittest.TestCase): self.assertTrue(not (x > y)) self.assertTrue(not (x >= y)) + def test_copy_sign(self): + d = Decimal(1).copy_sign(Decimal(-2)) + + self.assertEqual(Decimal(1).copy_sign(-2), d) + self.assertRaises(TypeError, Decimal(1).copy_sign, '-2') + # The following are two functions used to test threading in the next class def thfunc1(cls): @@ -1684,6 +1690,438 @@ class ContextAPItests(unittest.TestCase): self.assertNotEqual(id(c.flags), id(d.flags)) self.assertNotEqual(id(c.traps), id(d.traps)) + def test_abs(self): + c = Context() + d = c.abs(Decimal(-1)) + self.assertEqual(c.abs(-1), d) + self.assertRaises(TypeError, c.abs, '-1') + + def test_add(self): + c = Context() + d = c.add(Decimal(1), Decimal(1)) + self.assertEqual(c.add(1, 1), d) + self.assertEqual(c.add(Decimal(1), 1), d) + self.assertEqual(c.add(1, Decimal(1)), d) + self.assertRaises(TypeError, c.add, '1', 1) + self.assertRaises(TypeError, c.add, 1, '1') + + def test_compare(self): + c = Context() + d = c.compare(Decimal(1), Decimal(1)) + self.assertEqual(c.compare(1, 1), d) + self.assertEqual(c.compare(Decimal(1), 1), d) + self.assertEqual(c.compare(1, Decimal(1)), d) + self.assertRaises(TypeError, c.compare, '1', 1) + self.assertRaises(TypeError, c.compare, 1, '1') + + def test_compare_signal(self): + c = Context() + d = c.compare_signal(Decimal(1), Decimal(1)) + self.assertEqual(c.compare_signal(1, 1), d) + self.assertEqual(c.compare_signal(Decimal(1), 1), d) + self.assertEqual(c.compare_signal(1, Decimal(1)), d) + self.assertRaises(TypeError, c.compare_signal, '1', 1) + self.assertRaises(TypeError, c.compare_signal, 1, '1') + + def test_compare_total(self): + c = Context() + d = c.compare_total(Decimal(1), Decimal(1)) + self.assertEqual(c.compare_total(1, 1), d) + self.assertEqual(c.compare_total(Decimal(1), 1), d) + self.assertEqual(c.compare_total(1, Decimal(1)), d) + self.assertRaises(TypeError, c.compare_total, '1', 1) + self.assertRaises(TypeError, c.compare_total, 1, '1') + + def test_compare_total_mag(self): + c = Context() + d = c.compare_total_mag(Decimal(1), Decimal(1)) + self.assertEqual(c.compare_total_mag(1, 1), d) + self.assertEqual(c.compare_total_mag(Decimal(1), 1), d) + self.assertEqual(c.compare_total_mag(1, Decimal(1)), d) + self.assertRaises(TypeError, c.compare_total_mag, '1', 1) + self.assertRaises(TypeError, c.compare_total_mag, 1, '1') + + def test_copy_abs(self): + c = Context() + d = c.copy_abs(Decimal(-1)) + self.assertEqual(c.copy_abs(-1), d) + self.assertRaises(TypeError, c.copy_abs, '-1') + + def test_copy_decimal(self): + c = Context() + d = c.copy_decimal(Decimal(-1)) + self.assertEqual(c.copy_decimal(-1), d) + self.assertRaises(TypeError, c.copy_decimal, '-1') + + def test_copy_negate(self): + c = Context() + d = c.copy_negate(Decimal(-1)) + self.assertEqual(c.copy_negate(-1), d) + self.assertRaises(TypeError, c.copy_negate, '-1') + + def test_copy_sign(self): + c = Context() + d = c.copy_sign(Decimal(1), Decimal(-2)) + self.assertEqual(c.copy_sign(1, -2), d) + self.assertEqual(c.copy_sign(Decimal(1), -2), d) + self.assertEqual(c.copy_sign(1, Decimal(-2)), d) + self.assertRaises(TypeError, c.copy_sign, '1', -2) + self.assertRaises(TypeError, c.copy_sign, 1, '-2') + + def test_divide(self): + c = Context() + d = c.divide(Decimal(1), Decimal(2)) + self.assertEqual(c.divide(1, 2), d) + self.assertEqual(c.divide(Decimal(1), 2), d) + self.assertEqual(c.divide(1, Decimal(2)), d) + self.assertRaises(TypeError, c.divide, '1', 2) + self.assertRaises(TypeError, c.divide, 1, '2') + + def test_divide_int(self): + c = Context() + d = c.divide_int(Decimal(1), Decimal(2)) + self.assertEqual(c.divide_int(1, 2), d) + self.assertEqual(c.divide_int(Decimal(1), 2), d) + self.assertEqual(c.divide_int(1, Decimal(2)), d) + self.assertRaises(TypeError, c.divide_int, '1', 2) + self.assertRaises(TypeError, c.divide_int, 1, '2') + + def test_divmod(self): + c = Context() + d = c.divmod(Decimal(1), Decimal(2)) + self.assertEqual(c.divmod(1, 2), d) + self.assertEqual(c.divmod(Decimal(1), 2), d) + self.assertEqual(c.divmod(1, Decimal(2)), d) + self.assertRaises(TypeError, c.divmod, '1', 2) + self.assertRaises(TypeError, c.divmod, 1, '2') + + def test_exp(self): + c = Context() + d = c.exp(Decimal(10)) + self.assertEqual(c.exp(10), d) + self.assertRaises(TypeError, c.exp, '10') + + def test_fma(self): + c = Context() + d = c.fma(Decimal(2), Decimal(3), Decimal(4)) + self.assertEqual(c.fma(2, 3, 4), d) + self.assertEqual(c.fma(Decimal(2), 3, 4), d) + self.assertEqual(c.fma(2, Decimal(3), 4), d) + self.assertEqual(c.fma(2, 3, Decimal(4)), d) + self.assertEqual(c.fma(Decimal(2), Decimal(3), 4), d) + self.assertRaises(TypeError, c.fma, '2', 3, 4) + self.assertRaises(TypeError, c.fma, 2, '3', 4) + self.assertRaises(TypeError, c.fma, 2, 3, '4') + + def test_is_finite(self): + c = Context() + d = c.is_finite(Decimal(10)) + self.assertEqual(c.is_finite(10), d) + self.assertRaises(TypeError, c.is_finite, '10') + + def test_is_infinite(self): + c = Context() + d = c.is_infinite(Decimal(10)) + self.assertEqual(c.is_infinite(10), d) + self.assertRaises(TypeError, c.is_infinite, '10') + + def test_is_nan(self): + c = Context() + d = c.is_nan(Decimal(10)) + self.assertEqual(c.is_nan(10), d) + self.assertRaises(TypeError, c.is_nan, '10') + + def test_is_normal(self): + c = Context() + d = c.is_normal(Decimal(10)) + self.assertEqual(c.is_normal(10), d) + self.assertRaises(TypeError, c.is_normal, '10') + + def test_is_qnan(self): + c = Context() + d = c.is_qnan(Decimal(10)) + self.assertEqual(c.is_qnan(10), d) + self.assertRaises(TypeError, c.is_qnan, '10') + + def test_is_signed(self): + c = Context() + d = c.is_signed(Decimal(10)) + self.assertEqual(c.is_signed(10), d) + self.assertRaises(TypeError, c.is_signed, '10') + + def test_is_snan(self): + c = Context() + d = c.is_snan(Decimal(10)) + self.assertEqual(c.is_snan(10), d) + self.assertRaises(TypeError, c.is_snan, '10') + + def test_is_subnormal(self): + c = Context() + d = c.is_subnormal(Decimal(10)) + self.assertEqual(c.is_subnormal(10), d) + self.assertRaises(TypeError, c.is_subnormal, '10') + + def test_is_zero(self): + c = Context() + d = c.is_zero(Decimal(10)) + self.assertEqual(c.is_zero(10), d) + self.assertRaises(TypeError, c.is_zero, '10') + + def test_ln(self): + c = Context() + d = c.ln(Decimal(10)) + self.assertEqual(c.ln(10), d) + self.assertRaises(TypeError, c.ln, '10') + + def test_log10(self): + c = Context() + d = c.log10(Decimal(10)) + self.assertEqual(c.log10(10), d) + self.assertRaises(TypeError, c.log10, '10') + + def test_logb(self): + c = Context() + d = c.logb(Decimal(10)) + self.assertEqual(c.logb(10), d) + self.assertRaises(TypeError, c.logb, '10') + + def test_logical_and(self): + c = Context() + d = c.logical_and(Decimal(1), Decimal(1)) + self.assertEqual(c.logical_and(1, 1), d) + self.assertEqual(c.logical_and(Decimal(1), 1), d) + self.assertEqual(c.logical_and(1, Decimal(1)), d) + self.assertRaises(TypeError, c.logical_and, '1', 1) + self.assertRaises(TypeError, c.logical_and, 1, '1') + + def test_logical_invert(self): + c = Context() + d = c.logical_invert(Decimal(1000)) + self.assertEqual(c.logical_invert(1000), d) + self.assertRaises(TypeError, c.logical_invert, '1000') + + def test_logical_or(self): + c = Context() + d = c.logical_or(Decimal(1), Decimal(1)) + self.assertEqual(c.logical_or(1, 1), d) + self.assertEqual(c.logical_or(Decimal(1), 1), d) + self.assertEqual(c.logical_or(1, Decimal(1)), d) + self.assertRaises(TypeError, c.logical_or, '1', 1) + self.assertRaises(TypeError, c.logical_or, 1, '1') + + def test_logical_xor(self): + c = Context() + d = c.logical_xor(Decimal(1), Decimal(1)) + self.assertEqual(c.logical_xor(1, 1), d) + self.assertEqual(c.logical_xor(Decimal(1), 1), d) + self.assertEqual(c.logical_xor(1, Decimal(1)), d) + self.assertRaises(TypeError, c.logical_xor, '1', 1) + self.assertRaises(TypeError, c.logical_xor, 1, '1') + + def test_max(self): + c = Context() + d = c.max(Decimal(1), Decimal(2)) + self.assertEqual(c.max(1, 2), d) + self.assertEqual(c.max(Decimal(1), 2), d) + self.assertEqual(c.max(1, Decimal(2)), d) + self.assertRaises(TypeError, c.max, '1', 2) + self.assertRaises(TypeError, c.max, 1, '2') + + def test_max_mag(self): + c = Context() + d = c.max_mag(Decimal(1), Decimal(2)) + self.assertEqual(c.max_mag(1, 2), d) + self.assertEqual(c.max_mag(Decimal(1), 2), d) + self.assertEqual(c.max_mag(1, Decimal(2)), d) + self.assertRaises(TypeError, c.max_mag, '1', 2) + self.assertRaises(TypeError, c.max_mag, 1, '2') + + def test_min(self): + c = Context() + d = c.min(Decimal(1), Decimal(2)) + self.assertEqual(c.min(1, 2), d) + self.assertEqual(c.min(Decimal(1), 2), d) + self.assertEqual(c.min(1, Decimal(2)), d) + self.assertRaises(TypeError, c.min, '1', 2) + self.assertRaises(TypeError, c.min, 1, '2') + + def test_min_mag(self): + c = Context() + d = c.min_mag(Decimal(1), Decimal(2)) + self.assertEqual(c.min_mag(1, 2), d) + self.assertEqual(c.min_mag(Decimal(1), 2), d) + self.assertEqual(c.min_mag(1, Decimal(2)), d) + self.assertRaises(TypeError, c.min_mag, '1', 2) + self.assertRaises(TypeError, c.min_mag, 1, '2') + + def test_minus(self): + c = Context() + d = c.minus(Decimal(10)) + self.assertEqual(c.minus(10), d) + self.assertRaises(TypeError, c.minus, '10') + + def test_multiply(self): + c = Context() + d = c.multiply(Decimal(1), Decimal(2)) + self.assertEqual(c.multiply(1, 2), d) + self.assertEqual(c.multiply(Decimal(1), 2), d) + self.assertEqual(c.multiply(1, Decimal(2)), d) + self.assertRaises(TypeError, c.multiply, '1', 2) + self.assertRaises(TypeError, c.multiply, 1, '2') + + def test_next_minus(self): + c = Context() + d = c.next_minus(Decimal(10)) + self.assertEqual(c.next_minus(10), d) + self.assertRaises(TypeError, c.next_minus, '10') + + def test_next_plus(self): + c = Context() + d = c.next_plus(Decimal(10)) + self.assertEqual(c.next_plus(10), d) + self.assertRaises(TypeError, c.next_plus, '10') + + def test_next_toward(self): + c = Context() + d = c.next_toward(Decimal(1), Decimal(2)) + self.assertEqual(c.next_toward(1, 2), d) + self.assertEqual(c.next_toward(Decimal(1), 2), d) + self.assertEqual(c.next_toward(1, Decimal(2)), d) + self.assertRaises(TypeError, c.next_toward, '1', 2) + self.assertRaises(TypeError, c.next_toward, 1, '2') + + def test_normalize(self): + c = Context() + d = c.normalize(Decimal(10)) + self.assertEqual(c.normalize(10), d) + self.assertRaises(TypeError, c.normalize, '10') + + def test_number_class(self): + c = Context() + self.assertEqual(c.number_class(123), c.number_class(Decimal(123))) + self.assertEqual(c.number_class(0), c.number_class(Decimal(0))) + self.assertEqual(c.number_class(-45), c.number_class(Decimal(-45))) + + def test_power(self): + c = Context() + d = c.power(Decimal(1), Decimal(4), Decimal(2)) + self.assertEqual(c.power(1, 4, 2), d) + self.assertEqual(c.power(Decimal(1), 4, 2), d) + self.assertEqual(c.power(1, Decimal(4), 2), d) + self.assertEqual(c.power(1, 4, Decimal(2)), d) + self.assertEqual(c.power(Decimal(1), Decimal(4), 2), d) + self.assertRaises(TypeError, c.power, '1', 4, 2) + self.assertRaises(TypeError, c.power, 1, '4', 2) + self.assertRaises(TypeError, c.power, 1, 4, '2') + + def test_plus(self): + c = Context() + d = c.plus(Decimal(10)) + self.assertEqual(c.plus(10), d) + self.assertRaises(TypeError, c.plus, '10') + + def test_quantize(self): + c = Context() + d = c.quantize(Decimal(1), Decimal(2)) + self.assertEqual(c.quantize(1, 2), d) + self.assertEqual(c.quantize(Decimal(1), 2), d) + self.assertEqual(c.quantize(1, Decimal(2)), d) + self.assertRaises(TypeError, c.quantize, '1', 2) + self.assertRaises(TypeError, c.quantize, 1, '2') + + def test_remainder(self): + c = Context() + d = c.remainder(Decimal(1), Decimal(2)) + self.assertEqual(c.remainder(1, 2), d) + self.assertEqual(c.remainder(Decimal(1), 2), d) + self.assertEqual(c.remainder(1, Decimal(2)), d) + self.assertRaises(TypeError, c.remainder, '1', 2) + self.assertRaises(TypeError, c.remainder, 1, '2') + + def test_remainder_near(self): + c = Context() + d = c.remainder_near(Decimal(1), Decimal(2)) + self.assertEqual(c.remainder_near(1, 2), d) + self.assertEqual(c.remainder_near(Decimal(1), 2), d) + self.assertEqual(c.remainder_near(1, Decimal(2)), d) + self.assertRaises(TypeError, c.remainder_near, '1', 2) + self.assertRaises(TypeError, c.remainder_near, 1, '2') + + def test_rotate(self): + c = Context() + d = c.rotate(Decimal(1), Decimal(2)) + self.assertEqual(c.rotate(1, 2), d) + self.assertEqual(c.rotate(Decimal(1), 2), d) + self.assertEqual(c.rotate(1, Decimal(2)), d) + self.assertRaises(TypeError, c.rotate, '1', 2) + self.assertRaises(TypeError, c.rotate, 1, '2') + + def test_sqrt(self): + c = Context() + d = c.sqrt(Decimal(10)) + self.assertEqual(c.sqrt(10), d) + self.assertRaises(TypeError, c.sqrt, '10') + + def test_same_quantum(self): + c = Context() + d = c.same_quantum(Decimal(1), Decimal(2)) + self.assertEqual(c.same_quantum(1, 2), d) + self.assertEqual(c.same_quantum(Decimal(1), 2), d) + self.assertEqual(c.same_quantum(1, Decimal(2)), d) + self.assertRaises(TypeError, c.same_quantum, '1', 2) + self.assertRaises(TypeError, c.same_quantum, 1, '2') + + def test_scaleb(self): + c = Context() + d = c.scaleb(Decimal(1), Decimal(2)) + self.assertEqual(c.scaleb(1, 2), d) + self.assertEqual(c.scaleb(Decimal(1), 2), d) + self.assertEqual(c.scaleb(1, Decimal(2)), d) + self.assertRaises(TypeError, c.scaleb, '1', 2) + self.assertRaises(TypeError, c.scaleb, 1, '2') + + def test_shift(self): + c = Context() + d = c.shift(Decimal(1), Decimal(2)) + self.assertEqual(c.shift(1, 2), d) + self.assertEqual(c.shift(Decimal(1), 2), d) + self.assertEqual(c.shift(1, Decimal(2)), d) + self.assertRaises(TypeError, c.shift, '1', 2) + self.assertRaises(TypeError, c.shift, 1, '2') + + def test_subtract(self): + c = Context() + d = c.subtract(Decimal(1), Decimal(2)) + self.assertEqual(c.subtract(1, 2), d) + self.assertEqual(c.subtract(Decimal(1), 2), d) + self.assertEqual(c.subtract(1, Decimal(2)), d) + self.assertRaises(TypeError, c.subtract, '1', 2) + self.assertRaises(TypeError, c.subtract, 1, '2') + + def test_to_eng_string(self): + c = Context() + d = c.to_eng_string(Decimal(10)) + self.assertEqual(c.to_eng_string(10), d) + self.assertRaises(TypeError, c.to_eng_string, '10') + + def test_to_sci_string(self): + c = Context() + d = c.to_sci_string(Decimal(10)) + self.assertEqual(c.to_sci_string(10), d) + self.assertRaises(TypeError, c.to_sci_string, '10') + + def test_to_integral_exact(self): + c = Context() + d = c.to_integral_exact(Decimal(10)) + self.assertEqual(c.to_integral_exact(10), d) + self.assertRaises(TypeError, c.to_integral_exact, '10') + + def test_to_integral_value(self): + c = Context() + d = c.to_integral_value(Decimal(10)) + self.assertEqual(c.to_integral_value(10), d) + self.assertRaises(TypeError, c.to_integral_value, '10') + class WithStatementTest(unittest.TestCase): # Can't do these as docstrings until Python 2.6 # as doctest can't handle __future__ statements @@ -150,6 +150,7 @@ Benjamin Collar Jeffery Collins Robert Collins Paul Colomiets +Juan José Conti Matt Conway David M. Cooke Greg Copeland @@ -242,6 +242,12 @@ C-API Library ------- +- Issue #7633: In the decimal module, Context class methods (with the + exception of canonical and is_canonical) now accept instances of int + and long wherever a Decimal instance is accepted, and implicitly + convert that argument to Decimal. Previously only some arguments + were converted. + - Issue #7835: shelve should no longer produce mysterious warnings during interpreter shutdown. |