Index: Lib/decimal.py =================================================================== --- Lib/decimal.py (revision 59800) +++ Lib/decimal.py (working copy) @@ -135,6 +135,7 @@ ] import copy as _copy +import numbers # Rounding ROUND_DOWN = 'ROUND_DOWN' @@ -497,7 +498,7 @@ ##### Decimal class ####################################################### -class Decimal(object): +class Decimal(numbers.Real, numbers.Inexact): """Floating point class for decimal arithmetic.""" __slots__ = ('_exp','_int','_sign', '_is_special') @@ -527,7 +528,7 @@ # and the Decimal constructor still deal with tuples of # digits. - self = object.__new__(cls) + self = numbers.Real.__new__(cls) # From a string # REs insist on real strings, so we can too. @@ -756,6 +757,12 @@ else: # self_adjusted < other_adjusted return -((-1)**self._sign) + def __lt__(self, other): + return self.__cmp__(other) < 0 + + def __le__(self, other): + return self.__cmp__(other) <= 0 + def __eq__(self, other): if not isinstance(other, (Decimal, int, long)): return NotImplemented @@ -1405,6 +1412,10 @@ def __int__(self): """Converts self to an int, truncating if necessary.""" + return int(self.__trunc__()) + + def __trunc__(self): + """Truncates self to an int or long.""" if self._is_special: if self._isnan(): context = getcontext() @@ -1420,10 +1431,52 @@ def __long__(self): """Converts to a long. - Equivalent to long(int(self)) + Equivalent to long(trunc(self)) """ - return long(self.__int__()) + return long(self.__trunc__()) + def __floor__(self): + """Finds the greatest int <= self. + + This method won't affect math.floor() until 3.0. + + """ + return trunc(self._rescale(0, ROUND_FLOOR)) + + def __ceil__(self): + """Finds the least int >= self. + + This method won't affect math.ceil() until 3.0. + + """ + return trunc(self._rescale(0, ROUND_CEILING)) + + def __round__(self, ndigits=None): + """Rounds self to ndigits decimal places, defaulting to 0. + + If ndigits is omitted or None, returns an int, otherwise a + Decimal. Rounds according to the current context's rounding + mode, but does not signal any flags. + + When ndigits is passed, this method can remove significant + digits but cannot add any. So, for example: + + >>> Decimal("123.996").__round__(1) + Decimal("124.0") + >>> Decimal("12e1").__round__(3) + Decimal("1.2E+2") + + This method won't affect round() until 3.0. + + """ + rounding = getcontext().rounding + if ndigits is None: + return trunc(self._rescale(0, rounding)) + if self._exp >= -ndigits: + # Avoid adding significant digits. + return Decimal(self) + return self._rescale(-ndigits, rounding) + def _fix_nan(self, context): """Decapitate the payload of a NaN to fit the context""" payload = self._int Index: Lib/numbers.py =================================================================== --- Lib/numbers.py (revision 59800) +++ Lib/numbers.py (working copy) @@ -45,7 +45,6 @@ Inexact.register(complex) Inexact.register(float) -# Inexact.register(decimal.Decimal) class Complex(Number): @@ -257,7 +256,6 @@ return +self Real.register(float) -# Real.register(decimal.Decimal) class Rational(Real, Exact): Index: Lib/test/test_decimal.py =================================================================== --- Lib/test/test_decimal.py (revision 59800) +++ Lib/test/test_decimal.py (working copy) @@ -27,6 +27,8 @@ import unittest import glob +import math +import numbers import os, sys import pickle, copy from decimal import * @@ -829,7 +831,68 @@ self.assertEqual(-Decimal(45), Decimal(-45)) # - self.assertEqual(abs(Decimal(45)), abs(Decimal(-45))) # abs + def test_trunc(self): + self.assertEqual(int, type(trunc(Decimal("17.5")))) + self.assertEqual(17, trunc(Decimal("17"))) + self.assertEqual(17, trunc(Decimal("17.0001"))) + self.assertEqual(17, trunc(Decimal("17.999999"))) + self.assertEqual(-17, trunc(Decimal("-17.999999"))) + self.assertEqual(10**30, trunc(Decimal("1e30"))) + def test_floor(self): + # Will be int in py3k: + self.assertEqual(float, type(math.floor(Decimal("17.1")))) + self.assertEqual(int, type(Decimal("17.1").__floor__())) + self.assertEqual(17, Decimal("17").__floor__()) + self.assertEqual(17, Decimal("17.0001").__floor__()) + self.assertEqual(17, Decimal("17.999999").__floor__()) + self.assertEqual(-18, Decimal("-17.0001").__floor__()) + self.assertEqual(10**30, Decimal("1e30").__floor__()) + + def test_ceil(self): + # Will be int in py3k: + self.assertEqual(float, type(math.ceil(Decimal("17.1")))) + self.assertEqual(int, type(Decimal("17.1").__ceil__())) + self.assertEqual(17, Decimal("17").__ceil__()) + self.assertEqual(18, Decimal("17.0001").__ceil__()) + self.assertEqual(18, Decimal("17.999999").__ceil__()) + self.assertEqual(-17, Decimal("-17.0001").__ceil__()) + self.assertEqual(10**30, Decimal("1e30").__ceil__()) + + def test_round(self): + self.assertEqual(Decimal, type(Decimal("17").__round__(0))) + self.assertEqual(int, type(Decimal("17.5").__round__())) + # Will be Decimal in py3k: + self.assertEqual(float, type(round(Decimal("17"), 0))) + # Will be int in py3k: + self.assertEqual(float, type(round(Decimal("17.5")))) + + self.assertEqual(18, Decimal("17.5").__round__()) + self.assertEqual(16, Decimal("16.5").__round__()) + self.assertEqual(17, Decimal("17.4").__round__()) + self.assertEqual(10**30, Decimal("1e30").__round__()) + + self.assertEqual((0, (1, 7, 5), -1), + Decimal("17.5").__round__(1).as_tuple()) + self.assertEqual((0, (1, 2), 1), + Decimal("123.45").__round__(-1).as_tuple()) + self.assertEqual((0, (1, 2), -1), + Decimal("1.2").__round__(3).as_tuple()) + self.assertEqual((0, (1,), 30), + Decimal("1e30").__round__(0).as_tuple()) + + def test_round_sensitive_to_context_rounding(self): + with localcontext() as ctx: + ctx.rounding = ROUND_HALF_DOWN + self.assertEqual(17, Decimal("17.5").__round__()) + self.assertEqual(Decimal("-1.1"), Decimal("-1.15").__round__(1)) + + ctx.rounding = ROUND_CEILING + # XXX: Is this the right behavior? + self.assertEqual(-1, Decimal("-1.999").__round__()) + self.assertEqual(Decimal("1.3e2"), Decimal("120.0001").__round__(-1)) + + # The following are two functions used to test threading in the next class def thfunc1(cls): @@ -1172,7 +1235,13 @@ d = d1.max(d2) self.assertTrue(type(d) is Decimal) + def test_base_classes(self): + self.assertTrue(issubclass(Decimal, numbers.Real)) + self.assertTrue(issubclass(Decimal, numbers.Inexact)) + self.assertFalse(issubclass(Decimal, numbers.Rational)) + self.assertFalse(issubclass(Decimal, numbers.Exact)) + class DecimalPythonAPItests(unittest.TestCase): def test_pickle(self):