<p dir="auto">I was doing some testing using unsigned and signed integers with NumPy 1.20.3, and found out that when you multiply them with each other, it results in a 64-bit float.<br>
Same goes for addition/subtraction.<br>
As far as I know, this was not the case for earlier NumPy versions (as currently many parts of my code no longer work that previously worked just fine with this).<br>
To me, it sounds a bit strange that multiplying two integers with each other results in a float.</p>
<h3 dir="auto">Reproducing code example:</h3>

<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="import numpy as np
print(type(np.uint(1)*1))
&gt;&gt;&gt; &lt;class 'numpy.float64'&gt;"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-s1">numpy</span> <span class="pl-k">as</span> <span class="pl-s1">np</span>
<span class="pl-en">print</span>(<span class="pl-en">type</span>(<span class="pl-s1">np</span>.<span class="pl-en">uint</span>(<span class="pl-c1">1</span>)<span class="pl-c1">*</span><span class="pl-c1">1</span>))
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-c1">&lt;</span><span class="pl-k">class</span> <span class="pl-s">'numpy.float64'</span><span class="pl-c1">&gt;</span></pre></div>

<h3 dir="auto">NumPy/Python version information:</h3>
<p dir="auto">Python: 3.8.8<br>
NumPy: 1.20.3</p>
<h1 dir="auto">Describe the issue:</h1>
<h2 dir="auto">Expected Behavior:</h2>
<p dir="auto">I expect to create a python <code class="notranslate">Fraction</code> from numpy integer types with no loss of precision</p>
<h2 dir="auto">Actual Behavior:</h2>
<p dir="auto">Types are coerced to <code class="notranslate">numpy.float64</code> and precision is lost</p>
<h1 dir="auto">Reproducible code example:</h1>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="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)) # &lt;class 'numpy.float64'&gt;"><pre class="notranslate"><span class="pl-k">import</span> <span class="pl-s1">numpy</span> <span class="pl-k">as</span> <span class="pl-s1">np</span>
<span class="pl-k">from</span> <span class="pl-s1">fractions</span> <span class="pl-k">import</span> <span class="pl-v">Fraction</span>

<span class="pl-s1">nums</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">array</span>([<span class="pl-c1">1</span>])
<span class="pl-s1">denoms</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">array</span>([<span class="pl-c1">18446744073709550592</span>], <span class="pl-s1">dtype</span><span class="pl-c1">=</span><span class="pl-s">'uint64'</span>)
<span class="pl-s1">f</span> <span class="pl-c1">=</span> <span class="pl-v">Fraction</span>(<span class="pl-s1">nums</span>[<span class="pl-c1">0</span>], <span class="pl-s1">denoms</span>[<span class="pl-c1">0</span>]) <span class="pl-c"># denom has been rounded, is now a float</span>
<span class="pl-en">print</span>(<span class="pl-s1">f</span>) <span class="pl-c"># 1.0/1.8446744073709552e+19</span>
<span class="pl-en">print</span>(<span class="pl-en">type</span>(<span class="pl-s1">f</span>.<span class="pl-s1">denominator</span>)) <span class="pl-c"># &lt;class 'numpy.float64'&gt;</span></pre></div>
<h1 dir="auto">Error message:</h1>
<p dir="auto"><em>No response</em></p>
<h1 dir="auto">NumPy/Python version information:</h1>
<p dir="auto">Numpy: 1.21.2<br>
Python: 3.7.9 (default, Aug 31 2020, 12:42:55) [GCC 7.3.0]<br>
OS: Debian GNU/Linux 10 (buster)</p>
<h1 dir="auto">Other information that may be helpful:</h1>
<p dir="auto">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 <code class="notranslate">uint64</code>, despite the original value being valid. In my case I have a workaround, as converting the values to python <code class="notranslate">int</code> before inputting them to the <code class="notranslate">Fraction</code> 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.</p>
<p dir="auto">I briefly looked into the <code class="notranslate">Fractions</code> constructor and I discovered that this behavior comes from lines 175-180 of <code class="notranslate">fractions.py</code>:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="        elif (isinstance(numerator, numbers.Rational) and
            isinstance(denominator, numbers.Rational)):
            numerator, denominator = (
                numerator.numerator * denominator.denominator,
                denominator.numerator * numerator.denominator
                )"><pre class="notranslate">        <span class="pl-en">elif</span> (<span class="pl-en">isinstance</span>(<span class="pl-s1">numerator</span>, <span class="pl-s1">numbers</span>.<span class="pl-v">Rational</span>) <span class="pl-c1">and</span>
            <span class="pl-en">isinstance</span>(<span class="pl-s1">denominator</span>, <span class="pl-s1">numbers</span>.<span class="pl-v">Rational</span>)):
            <span class="pl-s1">numerator</span>, <span class="pl-s1">denominator</span> <span class="pl-c1">=</span> (
                <span class="pl-s1">numerator</span>.<span class="pl-s1">numerator</span> <span class="pl-c1">*</span> <span class="pl-s1">denominator</span>.<span class="pl-s1">denominator</span>,
                <span class="pl-s1">denominator</span>.<span class="pl-s1">numerator</span> <span class="pl-c1">*</span> <span class="pl-s1">numerator</span>.<span class="pl-s1">denominator</span>
                )</pre></div>
<p dir="auto"><code class="notranslate">numpy.uint64</code> is an instance of <code class="notranslate">numbers.Rational</code>:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="&gt;&gt;&gt; import numpy as np
&gt;&gt;&gt; import numbers
&gt;&gt;&gt; denoms = np.array([18446744073709550592], dtype='uint64')
&gt;&gt;&gt; isinstance(denoms[0], numbers.Rational)
True"><pre class="notranslate"><span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-k">import</span> <span class="pl-s1">numpy</span> <span class="pl-k">as</span> <span class="pl-s1">np</span>
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-k">import</span> <span class="pl-s1">numbers</span>
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-s1">denoms</span> <span class="pl-c1">=</span> <span class="pl-s1">np</span>.<span class="pl-en">array</span>([<span class="pl-c1">18446744073709550592</span>], <span class="pl-s1">dtype</span><span class="pl-c1">=</span><span class="pl-s">'uint64'</span>)
<span class="pl-c1">&gt;&gt;</span><span class="pl-c1">&gt;</span> <span class="pl-en">isinstance</span>(<span class="pl-s1">denoms</span>[<span class="pl-c1">0</span>], <span class="pl-s1">numbers</span>.<span class="pl-v">Rational</span>)
<span class="pl-c1">True</span></pre></div>
<p dir="auto">Inspecting the reproducible snippet above with <code class="notranslate">pdb</code> reveals that the multiplication on lines 178-179 of <code class="notranslate">fractions.py</code> is the moment when the type is changed.</p>
<p dir="auto">Thank you for your time (:</p>