当计算的输入是具有32位整数数据类型的numpy数组,但输出包含需要64位表示的较大数字时,我遇到了numpy计算不正确的问题。
这是一个最小的工作示例:
arr = np.ones(5, dtype=int) * (2**24 + 300) # arr.dtype defaults to 'int32'
# Following comment from @hpaulj I changed the first line, which was originally:
# arr = np.zeros(5, dtype=int)
# arr[:] = 2**24 + 300
single_value_calc = 2**8 * (2**24 + 300)
numpy_calc = 2**8 * arr
print(single_value_calc)
print(numpy_calc[0])
# RESULTS
4295044096
76800
所需的输出是numpy数组包含正确的值4295044096,这需要64位来表示它。也就是说,我希望numpy数组在输出需要时自动将其从int32转换为int64,而不是保持32位输出并在超过2 ^ 32的值后换回0。
当然,我可以通过强制使用int64表示来手动解决此问题:
numpy_calc2 = 2**8 * arr.astype('int64')
,但是对于一般代码来说,这是不希望的,因为在某些情况下(并非全部),输出仅需要64位表示(即保存大数)。在我的用例中,性能至关重要,因此每次都要强制进行上转换将耗资巨大。
这是numpy数组的预期行为吗?如果是这样,请问有没有一个干净,高效的解决方案?
答案 0 :(得分:2)
以numpy进行类型转换和提升相当复杂,有时会令人惊讶。 This recent unofficial write-up by Sebastian Berg解释了该主题的一些细微差别(主要集中在标量和0d数组)。
引用此文档
Python整数和浮点数
请注意,python整数的处理方式与numpy的处理方式完全相同。但是,它们的特殊之处在于它们没有显式关联的dtype。如此处所述,基于值的逻辑对于python整数和浮点数允许的使用似乎很有用:
arr = np.arange(10, dtype=np.int8) arr += 1 # or: res = arr + 1 res.dtype == np.int8
,以确保不会发生上流(例如内存使用率较高)。
(重点是我)
另请参见Allan Haldane's gist suggesting C-style type coercion,与上一文档链接:
当前,当二进制操作涉及两个dtype时,numpy的原理是“输出dtype的范围涵盖了两个输入dtype的范围”,并且当涉及单个dtype时,就不会进行任何强制转换。
strong>
(再次强调我的意思。)
所以我的理解是,numpy标量和数组的提升规则不同,主要是因为检查数组中的每个元素以确定是否可以安全地进行转换是不可行的。再次从以前的文件中获取:
基于标量的规则
与无法检查所有值的数组不同,对于标量(和0-D数组),将检查值。
这意味着您可以从一开始就使用np.int64
以确保安全(如果您使用的是Linux,则dtype=int
实际上将自己执行此操作),或者检查最大值在进行可疑操作之前先检查一下阵列,并根据情况确定是否必须自己提升dtype。我知道,如果您要进行大量计算,这可能不可行,但我认为考虑numpy当前的类型提升规则,这是没有办法的。