考虑以下简短的numpy会话,展示uint64
数据类型
import numpy as np
a = np.zeros(1,np.uint64)
a
# array([0], dtype=uint64)
a[0] -= 1
a
# array([18446744073709551615], dtype=uint64)
# this is 0xffff ffff ffff ffff, as expected
a[0] -= 1
a
# array([0], dtype=uint64)
# what the heck?
我完全被最后的输出搞糊涂了。
0xFFFF FFFF FFFF FFFF
这到底是怎么回事
我的设置:
>>> sys.platform
'linux'
>>> sys.version
'3.10.5 (main, Jul 20 2022, 08:58:47) [GCC 7.5.0]'
>>> np.version.version
'1.23.1'
5条答案
按热度按时间mgdq6dx11#
默认情况下,NumPy将Python int对象转换为
numpy.int_
,这是一个对应于Clong
的有符号整数dtype。(这个决定是在早期做出的,当时Pythonint
* 也 * 对应于Clong
。没有足够大的整数dtype来容纳
numpy.uint64
dtype * 和 *numpy.int_
dtype的所有值,因此numpy.uint64
标量和Python int对象之间的操作产生float 64结果而不是整数结果。(uint 64 arrays 和Python int之间的操作可能会有不同的行为,因为int会根据其值转换为dtype,但a[0]
是标量。第一次减法运算产生一个值为-1的float 64,第二次减法运算产生一个值为264的float 64(因为float 64没有足够的精度来精确地执行减法运算)。这两个值都超出了uint 64 dtype的范围,因此转换回uint 64以赋值给
a[0]
会产生undefined behavior(从C继承- NumPy只使用C强制转换)。在您的计算机上,这会产生回绕行为,因此-1回绕到18446744073709551615,264回绕到0,但这并不能保证。您可能会在其他设置中看到不同的行为。评论中的人确实看到了不同的行为。
mm5n2pyu2#
a[0] - 1
是1.8446744073709552e+19
,anumpy.float64
。它不能保留所有的精度,所以它的值是18446744073709551616=264。当用dtypenp.uint64
写回a
时,它变成0
。wpcxdonn3#
所有现有的答案都是正确的。我只是想在Windows 10上添加我得到了不同的结果,即9223372036854775808。
复制步骤:
在二进制中,增加1将把最后一位从0变为1,1变为0,从1变为0将改变最后一位之前的位,这将保持向左进位,直到最左边的位从0变为1。
在unit 64中,如果你想从0减去1,数字0不能变小,所以它被视为2^65,从它减去1得到2^65-1,在二进制中是'1'* 64,在十进制中是18446744073709551615。
然后,当使用Python
int
操作该值时,它被转换为float
1.8446744073709552e+19,由于格式的限制,实际上是2^64。现在这变得有趣了,uint 64可以保存的最大值是2^64 - 1,因为2^64在二进制中是1后跟64个0,所以它不能像uint 64中那样呈现,在这种情况下,它在递减之前被转换为0,因为2^64中的最后64位是0。
这就是为什么有一个警告。
但是在计算时,不知何故它被转换为有符号的int 64,然后再次转换为uint 64。
计算结果为-1,以带符号的int 64形式存储时为
'1'+'0'*63
,因为最左边的位用于符号,如果设置符号位,则数字为负。因为一位用于符号,所以int 64的最大值是2^63-1,十进制为9223372036854775807。
当int 64中的数字-1被转换为uint 64时,它被视为2^63,即十进制的9223372036854775808,因为该数字的数值为2^63。
然后,无论我做多少次递减,数字都保持不变,因为当操作发生时,uint 64类型被转换为浮点型,其值为2^63,并且递减1不会改变该值。
ujv3wf0j4#
可能的解决方法
1.显式强制转换
++
-
2.花式索引
+
-
3.切片索引
-
sd2nnvve5#
您看到的行为是由于numpy中的无符号整数运算的工作方式。当无符号整数递减时,如果结果为负,它将“回绕”到数据类型的最大值。
在您的示例中,a[0]从值0xFFFFFFFFFFFF开始,这是64位无符号整数的最大值。当您从中减去1时,结果是0xFFFFFFFFFFFFFE,正如您所期望的那样。但是,当您再次从中减去1时,结果为-1(在二进制中表示为0xFFFFFFFFFFFF)。由于此值为负,因此它会返回到数据类型的最大值,即0。
因此,您看到的行为是由于无符号整数算术的属性而预期的。如果要避免此行为,可以改用有符号整数数据类型。