numpy.array默认数据类型选择规则解析:混合整数为何转float64?
问题
当涉及2**64量级的数字且未指定数据类型时,numpy.array()函数的行为似乎颇为奇怪。能否有人解释这种行为?这是Bug还是预期表现?
$ python3 Python 3.10.8 (main, Nov 30 2022, 10:05:23) [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import numpy as np >>> np.__version__ '1.23.5' >>> np.array([2**63-1]).dtype dtype('int64') >>> np.array([2**64-1]).dtype dtype('uint64')
到目前为止,我认为这是合理的:int64可能是默认类型,但它无法表示2**64-1,因此切换为uint64。
>>> np.array([2**64-1, 2**63-1]).dtype dtype('float64')
但当我添加另一个同样完全适合uint64的数字时,类型却突然变为float64。
>>> np.array([2**65-1]).dtype dtype('O')
好吧,2**65-1需要更大的整数表示类型。
>>> np.array([2**64-1, 2**65-1]).dtype dtype('O')
这种行为不仅奇怪,还会影响精度:
>>> int(np.round(np.array([2**64-1])[0])) 18446744073709551615 >>> int(np.round(np.array([2**64-1, 2**63-1])[0])) 18446744073709551616 >>> int(np.round(np.array([2**64-1, 2**65-1])[0])) 18446744073709551615
在这三个表达式中,第一个和第三个的计算没有精度损失,但第二个由于转换为浮点数再转回整数而出现了舍入。为何第二个表达式会使用浮点数,而明明存在完全精确的整数类型可用?
解释
这是NumPy的类型提升规则导致的预期行为,并非Bug。核心原因在于NumPy推断数组 dtype 时,需要找到能容纳所有元素的公共类型,且遵循特定优先级逻辑:
单个元素的类型推断
- 仅包含
2**63-1时,该值是int64的最大值,NumPy直接推断为int64。 - 仅包含
2**64-1时,它超出int64范围,但刚好是uint64的最大值,因此推断为uint64。 - 仅包含
2**65-1时,它超出所有固定大小整数类型的范围,NumPy fallback 到object类型,直接存储Python原生整数。
- 仅包含
混合有符号/无符号整数的类型提升
当数组同时包含2**64-1(uint64)和2**63-1(int64)时:int64无法容纳uint64的最大值,而uint64虽然能存储2**63-1,但NumPy的类型提升规则不会直接将有符号整数和无符号整数的混合数组转为无符号类型——这是为了避免负数被意外转换为极大正数的风险。- 因此NumPy选择
float64作为公共类型。但float64只能精确表示小于2**53的整数,大于该值的整数会丢失精度,这就是第二个例子出现舍入错误的原因。
包含超大整数的混合类型
当数组同时包含2**64-1和2**65-1时,2**65-1无法用固定大小整数类型存储,NumPy直接将整个数组 dtype 设为object,保留原生Python整数,因此不会有精度损失。
如果需要强制使用精确的整数类型,可显式指定dtype参数:
np.array([2**64-1, 2**63-1], dtype='uint64')
内容的提问来源于stack exchange,提问作者PieterNuyts
相关产品推荐
相关产品推荐

