numpy 为什么加号和减号的推广规则不同,但结果相同?

ozxc1zmp  于 2023-08-05  发布在  其他
关注(0)|答案(1)|浏览(101)

我想知道为什么a - ba + (-b)给予了相同的结果,但在numpy中的类型不同:

import numpy as np

minuend = np.array(1, dtype=np.int64)
subtrahend = 1 << 63

result_minus = minuend - subtrahend
result_plus = minuend + (-subtrahend)

print(result_minus == result_plus)  # True
print(type(result_minus))  # float64
print(type(result_plus))  # int64

字符串
为什么会这样?我在哪里可以读到它?

kg7wmglp

kg7wmglp1#

关键是1 << 63不能用int64类型表示,但-(1 << 63)可以。这是一个病态的情况,来自于有符号整数如何以二进制(C2 representation)表示。
一方面,Numpy将subtrahend1 << 63)转换为uint64值,因为int64太小,无法保存该值。
另一方面,-subtrahend是由CPython计算的,因此它会产生一个包含-(1 << 63)的纯Python整数。然后Numpy将该值转换为int64值(因为此类型足够大,可以容纳该值)。
Numpy只在内部对相同类型的数组进行操作。涉及不同类型的二进制操作会导致数组提升(从C语言继承而来,主要是因为Numpy是用C编写的):Numpy转换输入数组的类型,因此目标二进制操作是有意义的,也是安全的(没有溢出,以在病态情况下可能损失精度为代价)。
在这种情况下,当执行减法时,Numpy选择将最终数组存储在float64数组中,因为对uint64int64数组进行二进制操作可能会导致溢出(无符号整数太大,无法存储在有符号整数中,负有符号整数无法表示为无符号整数)。当执行加法时,两个数组/值是相同类型的(即int64),因此不需要任何提升,结果数组的类型为int64
下面是一个方法来看看:

>>> np.int64(1 << 63)
---------------------------------------------------------------------------
OverflowError                             Traceback (most recent call last)
Cell In [7], line 1
----> 1 np.int64(1 << 63)

OverflowError: Python int too large to convert to C long

>>> np.int64(-(1 << 63))
-9223372036854775808

>>> np.array(subtrahend).dtype
dtype('uint64')

# Numpy first convert both to float64 arrays to avoid overflows in this specific case due to mixed uint64+int64 integer types
>>> (np.array(subtrahend) + minuend).dtype
dtype('float64')

>>> np.array(-subtrahend).dtype
dtype('int64')

字符串

相关问题