Skip to content

BUG: Binary operations between uint64 and intX results in float64  #20905

Open
@paigeweber13

Description

@paigeweber13

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 (:

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions