提问者:小点点

意外的uint64行为0xFFFF'FFFF'FFFF'FFFF-1=0?


考虑以下简短的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'

共2个答案

匿名用户

默认情况下,NumPy将Pythonint对象转换为numpy.int_,一个对应于Clong的有符号整数dtype。(这个决定是在Pythonint也对应于Clong的早期做出的。)

没有足够大的整数dtype来容纳numpy. uint64dtype和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]-11.8446744073709552e 19,一个numpy. float64。这不能保留所有的精度,所以它的值是18446744073709551616=264。当用dtypenp.uint64写回a时,它变成0