Почему (2 ^ 31) >> 32 не 0?

Моя проблема в том что

np.array([2**31], dtype=np.uint32) >> 32

не возвращает 0, но вместо этого возвращает array([2147483648], dtype=uint32). То же самое верно для

np.right_shift(np.array([2**31], dtype=np.uint32), 32)

(поэтому я считаю, что это просто, как >> реализовано).

Интересно, что все эти альтернативы, кажется, работают как ожидалось, возвращая некое значение 0:

print(
    2**31 >> 32,
    np.uint32(2**31) >> 32,
    np.array(2**31, dtype=np.uint32) >> 32,
    np.right_shift(2**31, 32),
    np.right_shift([2**31], 32),
    np.right_shift(np.uint32(2**31), 32),
    np.right_shift(np.array(2**31, dtype=np.uint32), 32),
)

В частности, что отличается между массивами Numpy, представляющими 2147483648 и [2147483648]?

Я видел эту проблему в JavaScript (почему << 32 не приводит к 0 в javascript?) И C++ (Странное поведение оператора сдвига вправо (1 >> 32), Почему 'int >> 32' не всегда ноль?), но еще не в Python/Numpy. На самом деле, ни документы Python, ни Numpy, похоже, не документируют это поведение:

Ответы

Ответ 1

Хотя это не задокументировано, numpy в основном реализовано в C, а оператор сдвига в C (и C++) не определен для сдвигов, больших или равных количеству битов. Так что результат может быть произвольным.

Если вы посмотрите на типы примеров, которые работают, вы поймете, почему они работают:

print(
    type(2**31 >> 32),
    type(np.uint32(2**31) >> 32),
    type(np.array(2**31, dtype=np.uint32) >> 32),
    type(np.right_shift(2**31, 32)),
    np.right_shift([2**31], 32).dtype,
    type(np.right_shift(np.uint32(2**31), 32)),
    type(np.right_shift(np.array(2**31, dtype=np.uint32), 32)),
)

<класс 'int'> <класс 'numpy.int64'> <класс 'numpy.int64'> <класс 'numpy.int64'> int64 <класс 'numpy.int64'> <класс 'numpy.int64'>

Первый использует собственный тип int Python, а все остальные преобразуются в numpy.int64, где поведение для 32-битного сдвига является правильным. В основном это связано с тем, что скалярные (нульмерные) массивы ведут себя по-разному. И в случае list что целочисленный тип по умолчанию для numpy не numpy.uint32.

С другой стороны

print((np.array([2**31], dtype=np.uint32) >> 32).dtype)

uint32

Таким образом, вы столкнулись с неопределенным поведением здесь.