为什么加号和减号的晋级规则不同,但结果是一样的?

ber*_*ers 10 numpy integer-promotion

我想知道为什么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
Run Code Online (Sandbox Code Playgroud)

为什么会这样?我在哪里可以读到相关内容?

Jér*_*ard 13

要点是1 << 63不能使用int64类型来表示,但-(1 << 63)可以。这是一个病态的情况,源于有符号整数如何以二进制表示(C2 表示)。

On one hand, Numpy converts subtrahend (1 << 63) to a uint64 value because int64 is too small to hold the value.

On another hand, -subtrahend is computed by CPython so it results in a pure-Python integer containing -(1 << 63). The value is then converted by Numpy to a int64 value (because this type is large enough to hold the value).

Numpy only operates on arrays of the same types internally. Binary operations involving different types result in array promotions (inherited from the C language mainly because Numpy is written in C) : Numpy converts the type of the input arrays so the target binary operation makes sense and is also safe (no overflow, at the expense of a possible loss of precision in pathological cases).

In this case, when the subtraction is performed, Numpy chooses to store the final array in a float64 array because a binary operation on both uint64 and int64 array is likely to cause overflows (unsigned integers are too big to be stored in signed ones and negative signed integer cannot be represented in unsigned ones). When the addition is performed, the two array/values are of the same type (i.e. int64), so there is no need for any promotion and the resulting array is of type int64.

Here is a way to see that:

>>> 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')
Run Code Online (Sandbox Code Playgroud)

  • @user2357112 是的,这句话实际上并不正确。我写了第一个(快速)答案,然后用更正确的“PS”部分更新了它,但忘记调整旧部分。我通过结合两个部分来完全重写答案,使其更加清晰和正确。谢谢。 (2认同)