Description
Describe the issue:
Expected Behavior:
I expect to create a python Fraction
from numpy integer types with no loss of precision
Actual Behavior:
Types are coerced to numpy.float64
and precision is lost
Reproducible code example:
import numpy as np
from fractions import Fraction
nums = np.array([1])
denoms = np.array([18446744073709550592], dtype='uint64')
f = Fraction(nums[0], denoms[0]) # denom has been rounded, is now a float
print(f) # 1.0/1.8446744073709552e+19
print(type(f.denominator)) # <class 'numpy.float64'>
Error message:
No response
NumPy/Python version information:
Numpy: 1.21.2
Python: 3.7.9 (default, Aug 31 2020, 12:42:55) [GCC 7.3.0]
OS: Debian GNU/Linux 10 (buster)
Other information that may be helpful:
I discovered this behavior while creating fractions from parallel arrays representing the numerators and denominators of fractions. This loss of precision is problematic because the float is rounded up so that it is out of range for uint64
, despite the original value being valid. In my case I have a workaround, as converting the values to python int
before inputting them to the Fraction
constructor prevents this issue. However, I wanted to let you know because it is unexpected that an operation with two numpy integer types produced a value with floating point types.
I briefly looked into the Fractions
constructor and I discovered that this behavior comes from lines 175-180 of fractions.py
:
elif (isinstance(numerator, numbers.Rational) and
isinstance(denominator, numbers.Rational)):
numerator, denominator = (
numerator.numerator * denominator.denominator,
denominator.numerator * numerator.denominator
)
numpy.uint64
is an instance of numbers.Rational
:
>>> import numpy as np
>>> import numbers
>>> denoms = np.array([18446744073709550592], dtype='uint64')
>>> isinstance(denoms[0], numbers.Rational)
True
Inspecting the reproducible snippet above with pdb
reveals that the multiplication on lines 178-179 of fractions.py
is the moment when the type is changed.
Thank you for your time (: