# HG changeset patch # User Robert Smallshire Exposes an is_integer() method on Decimal The required functionality was already implemented within the C implementation of Decimal, but was not previously exposed in the interface of Decimal. Although Decimal lies outside the purview of the numeric tower defined in numbers.py it is less surprising if Decimal supports the same interface as float and the other number types. Adds _pydecimal.Decimal.is_integer method implementation using the same strategy as the float implementation, a test for equality with floored self. Adds Decimal.is_integer tests Adds is_integer to the decimal context class Documentation for decimal.is_integer() diff --git a/Doc/library/decimal.rst b/Doc/library/decimal.rst index 6796f8a..26df44a 100644 --- a/Doc/library/decimal.rst +++ b/Doc/library/decimal.rst @@ -230,6 +230,8 @@ floating point flying circus: Decimal('2.5058') >>> c % a Decimal('0.77') + >>> a.is_integer() + False And some mathematical functions are also available to Decimal: @@ -610,6 +612,11 @@ Decimal objects Return :const:`True` if the argument is either positive or negative infinity and :const:`False` otherwise. + .. method:: is_integer() + + Return :const:`True` if the argument is a finite integral value and + :const:`False` otherwise. + .. method:: is_nan() Return :const:`True` if the argument is a (quiet or signaling) NaN and @@ -1201,6 +1208,10 @@ In addition to the three supplied contexts, new contexts can be created with the Returns ``True`` if *x* is infinite; otherwise returns ``False``. + .. method:: is_integer(x) + + Returns ``True`` if *x* is a finite integral value; otherwise + returns ``False``. .. method:: is_nan(x) diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index 60723f3..e95c176 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -3202,6 +3202,12 @@ class Decimal(object): """Return True if self is a zero; otherwise return False.""" return not self._is_special and self._int == '0' + def is_integer(self): + """Returns True is self is an integer, otherwise False.""" + if not self.is_finite(): + return False + return self.to_integral_value(ROUND_FLOOR) == self + def _ln_exp_bound(self): """Compute a lower bound for the adjusted exponent of self.ln(). In other words, compute r such that self.ln() >= 10**r. Assumes @@ -4697,6 +4703,23 @@ class Context(object): a = _convert_other(a, raiseit=True) return a.is_zero() + def is_integer(self, a): + """Return True if the operand is an integer; otherwise return False. + + >>> ExtendedContext.is_integer(Decimal('0')) + True + >>> ExtendedContext.is_integer(Decimal('2.50')) + False + >>> ExtendedContext.is_integer(Decimal('-0E+2')) + True + >>> ExtendedContext.is_integer(Decimal('-0.5')) + False + >>> ExtendedContext.is_integer(10) + True + """ + a = _convert_other(a, raiseit=True) + return a.is_integer() + def ln(self, a): """Returns the natural (base e) logarithm of the operand. diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index f56f9ad..afefd8f 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -2694,6 +2694,7 @@ class PythonAPItests(unittest.TestCase): self.assertRaises(TypeError, D(1).is_snan, context=xc) self.assertRaises(TypeError, D(1).is_signed, context=xc) self.assertRaises(TypeError, D(1).is_zero, context=xc) + self.assertRaises(TypeError, D(1).is_integer, context=xc) self.assertFalse(D("0.01").is_normal(context=xc)) self.assertTrue(D("0.01").is_subnormal(context=xc)) @@ -3163,6 +3164,15 @@ class ContextAPItests(unittest.TestCase): self.assertEqual(c.is_zero(10), d) self.assertRaises(TypeError, c.is_zero, '10') + def test_is_integer(self): + Decimal = self.decimal.Decimal + Context = self.decimal.Context + + c = Context() + d = c.is_integer(Decimal(10)) + self.assertEqual(c.is_integer(10), d) + self.assertRaises(TypeError, c.is_zero, '10') + def test_ln(self): Decimal = self.decimal.Decimal Context = self.decimal.Context @@ -4326,6 +4336,8 @@ class Coverage(unittest.TestCase): self.assertTrue(Decimal("-1").is_signed()) self.assertTrue(Decimal("0").is_zero()) self.assertTrue(Decimal("0").is_zero()) + self.assertTrue(Decimal("1").is_integer()) + self.assertFalse(Decimal("1.5").is_integer()) # Copy with localcontext() as c: diff --git a/Modules/_decimal/_decimal.c b/Modules/_decimal/_decimal.c index 3c2ad85..fda66f8 100644 --- a/Modules/_decimal/_decimal.c +++ b/Modules/_decimal/_decimal.c @@ -4110,6 +4110,7 @@ Dec_BoolFunc(mpd_isqnan) Dec_BoolFunc(mpd_issnan) Dec_BoolFunc(mpd_issigned) Dec_BoolFunc(mpd_iszero) +Dec_BoolFunc(mpd_isinteger) /* Boolean functions, optional context arg */ Dec_BoolFuncVA(mpd_isnormal) @@ -4750,6 +4751,7 @@ static PyMethodDef dec_methods [] = { "is_snan", dec_mpd_issnan, METH_NOARGS, doc_is_snan }, { "is_signed", dec_mpd_issigned, METH_NOARGS, doc_is_signed }, { "is_zero", dec_mpd_iszero, METH_NOARGS, doc_is_zero }, + { "is_integer", dec_mpd_isinteger, METH_NOARGS, doc_is_integer}, /* Boolean functions, optional context arg */ { "is_normal", (PyCFunction)dec_mpd_isnormal, METH_VARARGS|METH_KEYWORDS, doc_is_normal }, @@ -5161,6 +5163,7 @@ DecCtx_BoolFunc_NO_CTX(mpd_isqnan) DecCtx_BoolFunc_NO_CTX(mpd_issigned) DecCtx_BoolFunc_NO_CTX(mpd_issnan) DecCtx_BoolFunc_NO_CTX(mpd_iszero) +DecCtx_BoolFunc_NO_CTX(mpd_isinteger) static PyObject * ctx_iscanonical(PyObject *context UNUSED, PyObject *v) @@ -5442,6 +5445,7 @@ static PyMethodDef context_methods [] = { "is_snan", ctx_mpd_issnan, METH_O, doc_ctx_is_snan }, { "is_subnormal", ctx_mpd_issubnormal, METH_O, doc_ctx_is_subnormal }, { "is_zero", ctx_mpd_iszero, METH_O, doc_ctx_is_zero }, + { "is_integer", ctx_mpd_isinteger, METH_O, doc_ctx_is_integer }, /* Functions with a single decimal argument */ { "_apply", PyDecContext_Apply, METH_O, NULL }, /* alias for apply */ @@ -5866,5 +5870,3 @@ error: return NULL; /* GCOV_NOT_REACHED */ } - - diff --git a/Modules/_decimal/docstrings.h b/Modules/_decimal/docstrings.h index f7fd6e7..4e35917 100644 --- a/Modules/_decimal/docstrings.h +++ b/Modules/_decimal/docstrings.h @@ -260,6 +260,11 @@ Return True if the argument is a (positive or negative) zero and False\n\ otherwise.\n\ \n"); +PyDoc_STRVAR(doc_is_integer, +"is_integer($self, /)\n--\n\n\ +Return True if the argument is an integer and False otherwise.\n\ +\n"); + PyDoc_STRVAR(doc_ln, "ln($self, /, context=None)\n--\n\n\ Return the natural (base e) logarithm of the operand. The function always\n\ @@ -685,6 +690,11 @@ PyDoc_STRVAR(doc_ctx_is_zero, Return True if x is a zero, False otherwise.\n\ \n"); +PyDoc_STRVAR(doc_ctx_is_integer, +"is_integer($self, x, /)\n--\n\n\ +Return True if x is an integer, False otherwise.\n\ +\n"); + PyDoc_STRVAR(doc_ctx_ln, "ln($self, x, /)\n--\n\n\ Return the natural (base e) logarithm of x.\n\ @@ -879,6 +889,3 @@ Convert a number to a string using scientific notation.\n\ #endif /* DOCSTRINGS_H */ - - -