]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
Minor optimization for Fractions.limit_denominator (GH-93730)
authorMark Dickinson <dickinsm@gmail.com>
Tue, 21 Jun 2022 19:36:35 +0000 (20:36 +0100)
committerGitHub <noreply@github.com>
Tue, 21 Jun 2022 19:36:35 +0000 (21:36 +0200)
When we construct the upper and lower candidates in limit_denominator,
the numerator and denominator are already relatively prime (and the
denominator positive) by construction, so there's no need to go through
the usual normalisation in the constructor. This saves a couple of
potentially expensive gcd calls.

Suggested by Michael Scott Asato Cuthbert in GH-93477.

Lib/fractions.py

index f9ac882ec002fa4939948f0fd8c519e2963b4970..738a0d4c301d43e445e3dd4df51334dff908b53b 100644 (file)
@@ -245,14 +245,16 @@ class Fraction(numbers.Rational):
                 break
             p0, q0, p1, q1 = p1, q1, p0+a*p1, q2
             n, d = d, n-a*d
-
         k = (max_denominator-q0)//q1
-        bound1 = Fraction(p0+k*p1, q0+k*q1)
-        bound2 = Fraction(p1, q1)
-        if abs(bound2 - self) <= abs(bound1-self):
-            return bound2
+
+        # Determine which of the candidates (p0+k*p1)/(q0+k*q1) and p1/q1 is
+        # closer to self. The distance between them is 1/(q1*(q0+k*q1)), while
+        # the distance from p1/q1 to self is d/(q1*self._denominator). So we
+        # need to compare 2*(q0+k*q1) with self._denominator/d.
+        if 2*d*(q0+k*q1) <= self._denominator:
+            return Fraction(p1, q1, _normalize=False)
         else:
-            return bound1
+            return Fraction(p0+k*p1, q0+k*q1, _normalize=False)
 
     @property
     def numerator(a):