]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-68163: Correct conversion of Rational instances to float (GH-25619) (GH-96557)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Sun, 4 Sep 2022 12:40:24 +0000 (05:40 -0700)
committerGitHub <noreply@github.com>
Sun, 4 Sep 2022 12:40:24 +0000 (13:40 +0100)
* gh-68163: Correct conversion of Rational instances to float

Also document that numerator/denominator properties are instances of Integral.

Co-authored-by: Mark Dickinson <dickinsm@gmail.com>
(cherry picked from commit 8464b754c4168586b99e2135ccd2567e025625a9)

Co-authored-by: Sergey B Kirpichev <skirpichev@gmail.com>
Doc/library/numbers.rst
Lib/numbers.py
Lib/test/test_numeric_tower.py
Misc/NEWS.d/next/Library/2022-09-04-12-32-52.gh-issue-68163.h6TJCc.rst [new file with mode: 0644]

index b77845ed0dee92646146f831b19bfd71f21a1099..db06b08022ac26491b8001b58b9e5b1c9d0d57e6 100644 (file)
@@ -58,11 +58,14 @@ The numeric tower
 
 .. class:: Rational
 
-   Subtypes :class:`Real` and adds
-   :attr:`~Rational.numerator` and :attr:`~Rational.denominator` properties, which
-   should be in lowest terms. With these, it provides a default for
+   Subtypes :class:`Real` and adds :attr:`~Rational.numerator` and
+   :attr:`~Rational.denominator` properties. It also provides a default for
    :func:`float`.
 
+   The :attr:`~Rational.numerator` and :attr:`~Rational.denominator` values
+   should be instances of :class:`Integral` and should be in lowest terms with
+   :attr:`~Rational.denominator` positive.
+
    .. attribute:: numerator
 
       Abstract.
index 5b98e642083b36db66dfe99934b143fe20366b09..0985dd85f60a781603f867b7a4bede1c0b313fb1 100644 (file)
@@ -288,7 +288,7 @@ class Rational(Real):
         so that ratios of huge integers convert without overflowing.
 
         """
-        return self.numerator / self.denominator
+        return int(self.numerator) / int(self.denominator)
 
 
 class Integral(Rational):
index c54dedb8b793a0ad8d8a8a7351d3622d2242129e..9cd85e13634c2b4c4b6f25b8dce95b7f9d31ebc1 100644 (file)
@@ -14,6 +14,27 @@ from fractions import Fraction as F
 _PyHASH_MODULUS = sys.hash_info.modulus
 _PyHASH_INF = sys.hash_info.inf
 
+
+class DummyIntegral(int):
+    """Dummy Integral class to test conversion of the Rational to float."""
+
+    def __mul__(self, other):
+        return DummyIntegral(super().__mul__(other))
+    __rmul__ = __mul__
+
+    def __truediv__(self, other):
+        return NotImplemented
+    __rtruediv__ = __truediv__
+
+    @property
+    def numerator(self):
+        return DummyIntegral(self)
+
+    @property
+    def denominator(self):
+        return DummyIntegral(1)
+
+
 class HashTest(unittest.TestCase):
     def check_equal_hash(self, x, y):
         # check both that x and y are equal and that their hashes are equal
@@ -121,6 +142,13 @@ class HashTest(unittest.TestCase):
         self.assertEqual(hash(F(7*_PyHASH_MODULUS, 1)), 0)
         self.assertEqual(hash(F(-_PyHASH_MODULUS, 1)), 0)
 
+        # The numbers ABC doesn't enforce that the "true" division
+        # of integers produces a float.  This tests that the
+        # Rational.__float__() method has required type conversions.
+        x = F(DummyIntegral(1), DummyIntegral(2), _normalize=False)
+        self.assertRaises(TypeError, lambda: x.numerator/x.denominator)
+        self.assertEqual(float(x), 0.5)
+
     def test_hash_normalization(self):
         # Test for a bug encountered while changing long_hash.
         #
diff --git a/Misc/NEWS.d/next/Library/2022-09-04-12-32-52.gh-issue-68163.h6TJCc.rst b/Misc/NEWS.d/next/Library/2022-09-04-12-32-52.gh-issue-68163.h6TJCc.rst
new file mode 100644 (file)
index 0000000..756f6c9
--- /dev/null
@@ -0,0 +1 @@
+Correct conversion of :class:`numbers.Rational`'s to :class:`float`.