diff options
Diffstat (limited to 'Lib')
-rw-r--r-- | Lib/_pydecimal.py | 25 | ||||
-rw-r--r-- | Lib/numbers.py | 21 | ||||
-rw-r--r-- | Lib/test/decimaltestdata/extra.decTest | 18 | ||||
-rw-r--r-- | Lib/test/test_bool.py | 5 | ||||
-rw-r--r-- | Lib/test/test_decimal.py | 24 | ||||
-rw-r--r-- | Lib/test/test_fractions.py | 11 | ||||
-rw-r--r-- | Lib/test/test_long.py | 4 | ||||
-rw-r--r-- | Lib/test/test_numeric_tower.py | 31 |
8 files changed, 138 insertions, 1 deletions
diff --git a/Lib/_pydecimal.py b/Lib/_pydecimal.py index ab989e5..8c0ef57 100644 --- a/Lib/_pydecimal.py +++ b/Lib/_pydecimal.py @@ -3164,6 +3164,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): + """Return True is self is finite and integral; otherwise False.""" + if self._is_special: + return False + return self.to_integral_value(rounding=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 @@ -4659,6 +4665,25 @@ class Context(object): a = _convert_other(a, raiseit=True) return a.is_zero() + def is_integer(self, a): + """Return True if the operand is integral; 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(Decimal('NaN')) + 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/numbers.py b/Lib/numbers.py index ed815ef..0634f62 100644 --- a/Lib/numbers.py +++ b/Lib/numbers.py @@ -148,7 +148,7 @@ class Real(Complex): """To Complex, Real adds the operations that work on real numbers. In short, those are: a conversion to float, trunc(), divmod, - %, <, <=, >, and >=. + is_integer, %, <, <=, >, and >=. Real also provides defaults for the derived operations. """ @@ -242,6 +242,17 @@ class Real(Complex): """self <= other""" raise NotImplementedError + def is_integer(self): + """Return True if the Real is integral; otherwise return False. + + This default implementation can be overridden in subclasses + for performance reasons or to deal with values such as NaN, + which would otherwise cause an exception to be raised. + """ + # Although __int__ is not defined at this level, the int + # constructor falls back to __trunc__, which we do have. + return self == int(self) + # Concrete implementations of Complex abstract methods. def __complex__(self): """complex(self) == complex(float(self), 0)""" @@ -290,6 +301,10 @@ class Rational(Real): """ return self.numerator / self.denominator + def is_integer(self): + """Return True if the Rational is integral; otherwise return False.""" + return self.denominator == 1 + class Integral(Rational): """Integral adds a conversion to int and the bit-string operations.""" @@ -386,4 +401,8 @@ class Integral(Rational): """Integers have a denominator of 1.""" return 1 + def is_integer(self): + """Return True; all Integrals represent an integral value.""" + return True + Integral.register(int) diff --git a/Lib/test/decimaltestdata/extra.decTest b/Lib/test/decimaltestdata/extra.decTest index b630d8e..2f0719e 100644 --- a/Lib/test/decimaltestdata/extra.decTest +++ b/Lib/test/decimaltestdata/extra.decTest @@ -2346,6 +2346,24 @@ bool2096 iszero sNaN -> 0 bool2097 iszero -sNaN -> 0 bool2098 iszero sNaN123 -> 0 bool2099 iszero -sNaN123 -> 0 +bool2100 is_integer -1.0 -> 1 +bool2101 is_integer 0.0 -> 1 +bool2102 is_integer 1.0 -> 1 +bool2103 is_integer 42 -> 1 +bool2104 is_integer 1e2 -> 1 +bool2105 is_integer 1.5 -> 0 +bool2106 is_integer 1e-2 -> 0 +bool2107 is_integer NaN -> 0 +bool2109 is_integer -NaN -> 0 +bool2110 is_integer NaN123 -> 0 +bool2111 is_integer -NaN123 -> 0 +bool2112 is_integer sNaN -> 0 +bool2113 is_integer -sNaN -> 0 +bool2114 is_integer sNaN123 -> 0 +bool2115 is_integer -sNaN123 -> 0 +bool2116 is_integer Infinity -> 0 +bool2117 is_integer -Infinity -> 0 + ------------------------------------------------------------------------ -- The following tests (pwmx0 through pwmx440) are for the -- diff --git a/Lib/test/test_bool.py b/Lib/test/test_bool.py index 7b3a385..bc201e1 100644 --- a/Lib/test/test_bool.py +++ b/Lib/test/test_bool.py @@ -354,6 +354,11 @@ class BoolTest(unittest.TestCase): self.assertIs(type(False.real), int) self.assertIs(type(False.imag), int) + def test_always_is_integer(self): + # Issue #26680: Incorporating number.is_integer into bool + self.assertTrue(all(b.is_integer() for b in (False, True))) + + def test_main(): support.run_unittest(BoolTest) diff --git a/Lib/test/test_decimal.py b/Lib/test/test_decimal.py index dbd58e8..efb41fd 100644 --- a/Lib/test/test_decimal.py +++ b/Lib/test/test_decimal.py @@ -276,6 +276,7 @@ class IBMTestCases(unittest.TestCase): 'is_snan', 'is_subnormal', 'is_zero', + 'is_integer', 'same_quantum') def read_unlimited(self, v, context): @@ -2726,6 +2727,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)) @@ -3197,6 +3199,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() + b = c.is_integer(Decimal(10)) + self.assertEqual(c.is_integer(10), b) + self.assertRaises(TypeError, c.is_integer, '10') + def test_ln(self): Decimal = self.decimal.Decimal Context = self.decimal.Context @@ -4360,6 +4371,19 @@ 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.assertTrue(Decimal("0").is_integer()) + self.assertTrue(Decimal("1").is_integer()) + self.assertTrue(Decimal("42").is_integer()) + self.assertTrue(Decimal("1e2").is_integer()) + self.assertFalse(Decimal("1.5").is_integer()) + self.assertFalse(Decimal("1e-2").is_integer()) + self.assertFalse(Decimal("NaN").is_integer()) + self.assertFalse(Decimal("-NaN").is_integer()) + self.assertFalse(Decimal("sNaN").is_integer()) + self.assertFalse(Decimal("-sNaN").is_integer()) + self.assertFalse(Decimal("Inf").is_integer()) + self.assertFalse(Decimal("-Inf").is_integer()) # Copy with localcontext() as c: diff --git a/Lib/test/test_fractions.py b/Lib/test/test_fractions.py index 0845f79..811b58f 100644 --- a/Lib/test/test_fractions.py +++ b/Lib/test/test_fractions.py @@ -724,6 +724,17 @@ class FractionTest(unittest.TestCase): self.assertEqual(type(f.numerator), myint) self.assertEqual(type(f.denominator), myint) + def test_is_integer(self): + # Issue #26680: Incorporating number.is_integer into Fraction + self.assertTrue(F(-1, 1).is_integer()) + self.assertTrue(F(0, 1).is_integer()) + self.assertTrue(F(1, 1).is_integer()) + self.assertTrue(F(42, 1).is_integer()) + self.assertTrue(F(2, 2).is_integer()) + self.assertTrue(F(8, 4).is_integer()) + self.assertFalse(F(1, 2).is_integer()) + self.assertFalse(F(1, 3).is_integer()) + self.assertFalse(F(2, 3).is_integer()) if __name__ == '__main__': unittest.main() diff --git a/Lib/test/test_long.py b/Lib/test/test_long.py index c97842b..669826c 100644 --- a/Lib/test/test_long.py +++ b/Lib/test/test_long.py @@ -1381,6 +1381,10 @@ class LongTest(unittest.TestCase): self.assertEqual(type(numerator), int) self.assertEqual(type(denominator), int) + def test_int_always_is_integer(self): + # Issue #26680: Incorporating number.is_integer into int + self.assertTrue(all(x.is_integer() for x in (-1, 0, 1, 42))) + if __name__ == "__main__": unittest.main() diff --git a/Lib/test/test_numeric_tower.py b/Lib/test/test_numeric_tower.py index c54dedb..4e46aac 100644 --- a/Lib/test/test_numeric_tower.py +++ b/Lib/test/test_numeric_tower.py @@ -6,6 +6,7 @@ import math import sys import operator +from numbers import Real, Rational, Integral from decimal import Decimal as D from fractions import Fraction as F @@ -198,5 +199,35 @@ class ComparisonTest(unittest.TestCase): self.assertRaises(TypeError, op, v, z) +class IsIntegerTest(unittest.TestCase): + + def test_real_is_integer(self): + self.assertTrue(Real.is_integer(-1.0)) + self.assertTrue(Real.is_integer(0.0)) + self.assertTrue(Real.is_integer(1.0)) + self.assertTrue(Real.is_integer(42.0)) + + self.assertFalse(Real.is_integer(-0.5)) + self.assertFalse(Real.is_integer(4.2)) + + def test_rational_is_integer(self): + self.assertTrue(Rational.is_integer(F(-1, 1))) + self.assertTrue(Rational.is_integer(F(0, 1))) + self.assertTrue(Rational.is_integer(F(1, 1))) + self.assertTrue(Rational.is_integer(F(42, 1))) + self.assertTrue(Rational.is_integer(F(2, 2))) + self.assertTrue(Rational.is_integer(F(8, 4))) + + self.assertFalse(Rational.is_integer(F(1, 2))) + self.assertFalse(Rational.is_integer(F(1, 3))) + self.assertFalse(Rational.is_integer(F(2, 3))) + + def test_integral_is_integer(self): + self.assertTrue(Integral.is_integer(-1)) + self.assertTrue(Integral.is_integer(0)) + self.assertTrue(Integral.is_integer(1)) + self.assertTrue(Integral.is_integer(1729)) + + if __name__ == '__main__': unittest.main() |