考虑以下简短的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'FFFE。
这到底是怎么回事?
我的设置:
>>> 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'
默认情况下,NumPy将Pythonint对象转换为numpy.int_
,一个对应于Clong
的有符号整数dtype。(这个决定是在Pythonint
也对应于Clong
的早期做出的。)
没有足够大的整数dtype来容纳numpy. uint64
dtype和numpy的所有值。int_
dtype,所以numpy.uint64
标量和Pythonint对象之间的操作会产生float64结果而不是整数结果。(uint64数组和Pythonint之间的操作可能行为不同,因为在这些操作中int是根据其值转换为dtype的,但a[0]
是标量。)
第一次减法产生值为-1的float64,第二次减法产生值为2**64的float64(因为float64没有足够的精度来精确执行减法)。这两个值都超出了uint64 dtype的范围,因此转换回uint64以分配给a[0]
会产生未定义的行为(继承自C-NumPy只是使用C强制转换)。
在您的机器上,这恰好会产生环绕行为,因此-1环绕18446744073709551615,2**64环绕0,但这并不能保证。您可能会在其他设置上看到不同的行为。评论中的人确实看到了不同的行为。
a[0]-1
是1.8446744073709552e 19
,一个numpy. float64
。这不能保留所有的精度,所以它的值是18446744073709551616=264。当用dtypenp.uint64
写回a
时,它变成0
。