<p dir="auto">Preface: I'm not sure if this should be classified as a bug, expected behavior, or a longstanding inference rule that would be problematic to change now.</p>
<p dir="auto">From what I can tell numpy looks to be inferring <code class="notranslate">float64</code> instead of <code class="notranslate">uint64</code> when both of the following conditions are met:</p>
<ul dir="auto">
<li>input data contains a number supported by both <code class="notranslate">int64</code> and <code class="notranslate">uint64</code> (0 to 2**63 - 1)</li>
<li>input data contains a number supported by only <code class="notranslate">uint64</code> (2**63 and above)</li>
</ul>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="In [1]: import sys; import numpy as np; np.__version__, sys.version
Out[1]: 
('1.17.3',
 '3.7.5 (default, Oct 25 2019, 10:52:18) \n[Clang 4.0.1 (tags/RELEASE_401/final)]')

In [2]: np.array([0, 2**63])
Out[2]: array([0.00000000e+00, 9.22337204e+18])"><pre class="notranslate"><span class="pl-v">In</span> [<span class="pl-c1">1</span>]: <span class="pl-s1">import</span> <span class="pl-s1">sys</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-s1">np</span>.<span class="pl-s1">__version__</span>, <span class="pl-s1">sys</span>.<span class="pl-s1">version</span>
<span class="pl-v">Out</span>[<span class="pl-c1">1</span>]: 
(<span class="pl-s">'1.17.3'</span>,
 <span class="pl-s">'3.7.5 (default, Oct 25 2019, 10:52:18) <span class="pl-cce">\n</span>[Clang 4.0.1 (tags/RELEASE_401/final)]'</span>)

<span class="pl-v">In</span> [<span class="pl-c1">2</span>]: <span class="pl-s1">np</span>.<span class="pl-en">array</span>([<span class="pl-c1">0</span>, <span class="pl-c1">2</span><span class="pl-c1">**</span><span class="pl-c1">63</span>])
<span class="pl-v">Out</span>[<span class="pl-c1">2</span>]: <span class="pl-en">array</span>([<span class="pl-c1">0.00000000e+00</span>, <span class="pl-c1">9.22337204e+18</span>])</pre></div>
<p dir="auto">Note that dtype is correctly inferred as <code class="notranslate">uint64</code> when all values fall into the <code class="notranslate">uint64</code>-only range:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="In [3]: np.array([2**63, 2**63 + 1])
Out[3]: array([9223372036854775808, 9223372036854775809], dtype=uint64)"><pre class="notranslate"><span class="pl-v">In</span> [<span class="pl-c1">3</span>]: <span class="pl-s1">np</span>.<span class="pl-en">array</span>([<span class="pl-c1">2</span><span class="pl-c1">**</span><span class="pl-c1">63</span>, <span class="pl-c1">2</span><span class="pl-c1">**</span><span class="pl-c1">63</span> <span class="pl-c1">+</span> <span class="pl-c1">1</span>])
<span class="pl-v">Out</span>[<span class="pl-c1">3</span>]: <span class="pl-en">array</span>([<span class="pl-c1">9223372036854775808</span>, <span class="pl-c1">9223372036854775809</span>], <span class="pl-s1">dtype</span><span class="pl-c1">=</span><span class="pl-s1">uint64</span>)</pre></div>
<p dir="auto">Likewise, if all values are supported by both <code class="notranslate">int64</code> and <code class="notranslate">uint64</code> the dtype is correctly inferred as <code class="notranslate">int64</code>:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="In [4]: np.array([0, 2**63 - 1])
Out[4]: array([                  0, 9223372036854775807])"><pre class="notranslate"><span class="pl-v">In</span> [<span class="pl-c1">4</span>]: <span class="pl-s1">np</span>.<span class="pl-en">array</span>([<span class="pl-c1">0</span>, <span class="pl-c1">2</span><span class="pl-c1">**</span><span class="pl-c1">63</span> <span class="pl-c1">-</span> <span class="pl-c1">1</span>])
<span class="pl-v">Out</span>[<span class="pl-c1">4</span>]: <span class="pl-en">array</span>([                  <span class="pl-c1">0</span>, <span class="pl-c1">9223372036854775807</span>])</pre></div>
<p dir="auto">The <code class="notranslate">float64</code> inference is a bit surprising because data that's supported by an integer dtype is inferred as a non-integer dtype.  This can lead to problematic behavior because many <code class="notranslate">uint64</code>-only values cannot be represented exactly by <code class="notranslate">float64</code>, so roundtripping through <code class="notranslate">float64</code> can lead to precision loss:</p>
<div class="highlight highlight-source-python notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="In [5]: np.array([2**63 - 1, 2**63, 2**63 + 1]).astype('uint64')
Out[5]: 
array([9223372036854775808, 9223372036854775808, 9223372036854775808],
      dtype=uint64)"><pre class="notranslate"><span class="pl-v">In</span> [<span class="pl-c1">5</span>]: <span class="pl-s1">np</span>.<span class="pl-en">array</span>([<span class="pl-c1">2</span><span class="pl-c1">**</span><span class="pl-c1">63</span> <span class="pl-c1">-</span> <span class="pl-c1">1</span>, <span class="pl-c1">2</span><span class="pl-c1">**</span><span class="pl-c1">63</span>, <span class="pl-c1">2</span><span class="pl-c1">**</span><span class="pl-c1">63</span> <span class="pl-c1">+</span> <span class="pl-c1">1</span>]).<span class="pl-en">astype</span>(<span class="pl-s">'uint64'</span>)
<span class="pl-v">Out</span>[<span class="pl-c1">5</span>]: 
<span class="pl-en">array</span>([<span class="pl-c1">9223372036854775808</span>, <span class="pl-c1">9223372036854775808</span>, <span class="pl-c1">9223372036854775808</span>],
      <span class="pl-s1">dtype</span><span class="pl-c1">=</span><span class="pl-s1">uint64</span>)</pre></div>
<p dir="auto">Of course, the example above can be avoided by directly passing <code class="notranslate">dtype='uint64'</code> to <code class="notranslate">np.array</code> but from a user perspective it doesn't seem very intuitive to account for a corner case in inference logic.</p>
<p dir="auto">There are cases where the <code class="notranslate">float64</code> inference makes sense, e.g. <code class="notranslate">[-1, 2**63]</code>, but it seems like the example I pointed out shouldn't be terribly complicated to infer as <code class="notranslate">uint64</code>.  That being said, I don't know if this will cause cascading issues, or if this behavior is too longstanding to change.</p>
<p dir="auto">I expected to get uint64</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="umax = np.iinfo(np.uint64).max

np.array([umax]).dtype   # &lt;-- uint64, as expected

np.array([0, umax]).dtype  # &lt;-- float64, surprising"><pre class="notranslate"><code class="notranslate">umax = np.iinfo(np.uint64).max

np.array([umax]).dtype   # &lt;-- uint64, as expected

np.array([0, umax]).dtype  # &lt;-- float64, surprising
</code></pre></div>
<p dir="auto">There seems to be something special going on inference-wise around the int64 bound:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="imax = np.iinfo(np.int64).max

np.array([imax, umax]).dtype  # &lt;-- float64

np.array([imax+1, umax]).dtype  # &lt;-- uint64"><pre class="notranslate"><code class="notranslate">imax = np.iinfo(np.int64).max

np.array([imax, umax]).dtype  # &lt;-- float64

np.array([imax+1, umax]).dtype  # &lt;-- uint64
</code></pre></div>