-
-
Notifications
You must be signed in to change notification settings - Fork 33.2k
Closed as not planned
Closed as not planned
Copy link
Labels
type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Description
Consider the following operations between fractions:
>>> from fractions import Fraction
>>> import numpy as np
>>> np.seterr(all='ignore')
>>> a = Fraction(15.09288707969291)
>>> b = Fraction(24274009, 1604460)
>>> c = Fraction(np.int64(24274009), 1604460)
>>> b == c
True
>>> a - b
Fraction(-8173385384594606777, 225807670566589562880)
>>> a - c
Fraction(3404474268251672442183, 225807670566589562880)
The last two results should be equal.
Numpy integers qualify as Rational
in Fraction.__new__()
and the Fraction
instance uses them internally without modification:
class Fraction(numbers.Rational):
def __new__(cls, numerator=0, denominator=None, *, _normalize=True):
[...]
elif (isinstance(numerator, numbers.Rational) and
isinstance(denominator, numbers.Rational)):
numerator, denominator = (
numerator.numerator * denominator.denominator,
denominator.numerator * numerator.denominator
)
[...]
But they may overflow, as in the example, producing wrong results when np.seterr()
is set to warn or ignore.
The workaround for the user is as simple as converting inputs to Python integers, but it's not obvious that the user needs to do so.
A small change in the constructor like this would avoid the problem:
class Fraction(numbers.Rational):
def __new__(cls, numerator=0, denominator=None, *, _normalize=True):
[...]
elif (isinstance(numerator, numbers.Rational) and
isinstance(denominator, numbers.Rational)):
numerator, denominator = (
int(numerator.numerator) * int(denominator.denominator),
int(denominator.numerator) * int(numerator.denominator)
)
[...].
CPython versions tested on:
3.10, 3.12, 3.13
Operating systems tested on:
Linux
Metadata
Metadata
Assignees
Labels
type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error