bpo-39350: Fix fractions for int subclasses (GH-18375)
Fix regression in fractions.Fraction if the numerator and/or the denominator is an int subclass. The math.gcd() function is now used to normalize the numerator and denominator. math.gcd() always return a int type. Previously, the GCD type depended on numerator and denominator.
This commit is contained in:
parent
60ac6ed557
commit
dc7a50d73a
|
@ -84,6 +84,10 @@ another rational number, or from a string.
|
|||
The :class:`Fraction` constructor now accepts :class:`float` and
|
||||
:class:`decimal.Decimal` instances.
|
||||
|
||||
.. versionchanged:: 3.9
|
||||
The :func:`math.gcd` function is now used to normalize the *numerator*
|
||||
and *denominator*. :func:`math.gcd` always return a :class:`int` type.
|
||||
Previously, the GCD type depended on *numerator* and *denominator*.
|
||||
|
||||
.. attribute:: numerator
|
||||
|
||||
|
|
|
@ -155,13 +155,9 @@ class Fraction(numbers.Rational):
|
|||
if denominator == 0:
|
||||
raise ZeroDivisionError('Fraction(%s, 0)' % numerator)
|
||||
if _normalize:
|
||||
if type(numerator) is int is type(denominator):
|
||||
# *very* normal case
|
||||
g = math.gcd(numerator, denominator)
|
||||
if denominator < 0:
|
||||
g = -g
|
||||
else:
|
||||
g = _gcd(numerator, denominator)
|
||||
g = math.gcd(numerator, denominator)
|
||||
if denominator < 0:
|
||||
g = -g
|
||||
numerator //= g
|
||||
denominator //= g
|
||||
self._numerator = numerator
|
||||
|
|
|
@ -703,6 +703,28 @@ class FractionTest(unittest.TestCase):
|
|||
r = F(13, 7)
|
||||
self.assertRaises(AttributeError, setattr, r, 'a', 10)
|
||||
|
||||
def test_int_subclass(self):
|
||||
class myint(int):
|
||||
def __mul__(self, other):
|
||||
return type(self)(int(self) * int(other))
|
||||
def __floordiv__(self, other):
|
||||
return type(self)(int(self) // int(other))
|
||||
def __mod__(self, other):
|
||||
x = type(self)(int(self) % int(other))
|
||||
return x
|
||||
@property
|
||||
def numerator(self):
|
||||
return type(self)(int(self))
|
||||
@property
|
||||
def denominator(self):
|
||||
return type(self)(1)
|
||||
|
||||
f = fractions.Fraction(myint(1 * 3), myint(2 * 3))
|
||||
self.assertEqual(f.numerator, 1)
|
||||
self.assertEqual(f.denominator, 2)
|
||||
self.assertEqual(type(f.numerator), myint)
|
||||
self.assertEqual(type(f.denominator), myint)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
Fix regression in :class:`fractions.Fraction` if the numerator and/or the
|
||||
denominator is an :class:`int` subclass. The :func:`math.gcd` function is now
|
||||
used to normalize the *numerator* and *denominator*. :func:`math.gcd` always
|
||||
return a :class:`int` type. Previously, the GCD type depended on *numerator*
|
||||
and *denominator*.
|
Loading…
Reference in New Issue