Почему бит-мудрый сдвиг влево возвращает разные результаты в Python и Java?

Я пытаюсь перенести некоторые функции из приложения Java в Python.

В Java,

System.out.println(155 << 24);

Возвращает: -1694498816

В Python:

print(155 << 24)

Возвращает 2600468480

Многие другие побитовые операции работали одинаково на обоих языках. Почему в этих двух операциях есть другой результат?


EDIT: Я пытаюсь создать функцию в python для репликации работы левого оператора смены в Java. Что-то вроде:

def lshift(val, n):
    return (int(val) << n) - 0x100000000

Однако это не кажется правильным, поскольку (я думаю) он превращает все отрицательные числа чисел?


EDIT2: Несколько часов спустя я решил, что, вероятно, не самая лучшая идея использовать Python для этого задания и будет участвовать в Java-приложении и использовать его в качестве микросервиса для существующего приложения Python.

Ответ 1

Вот три разных способа преобразования целочисленного Python в эквивалентную Java-подпись int. Обратите внимание, что эти функции будут работать некорректно, если аргумент шире 32 бита, поэтому вы можете использовать бит-маскирование аргумента перед вызовом.

Первый способ - использовать модуль struct для интерпретации числа в виде 32-битного беззнакового целого числа, упаковать его в байты (используя соглашение с локальным концом), а затем распаковать эти байты, интерпретируя их как 32-битную подписанную целое число. Другие два метода используют простую арифметику без вызовов функций, поэтому они быстрее, но я думаю, их немного труднее читать.

Этот код был написан на 32-битной машине, на которой запущен Python 2.6.6, но он должен корректно работать в любой архитектуре и версии Python (если только это не очень древний:)).

from __future__ import print_function
from struct import pack, unpack

def ulong_to_long_pack(u):
    ''' using pack & unpack '''
    ubytes = pack('L', u)
    return unpack('l', ubytes)[0]

def ulong_to_long_sub(u):
    ''' using subtraction '''
    return u - (1<<32) if u >= (1<<31) else u

def ulong_to_long2_xor(u):
    ''' using exclusive OR '''
    return u ^ ~((1<<32)-1) if u & (1<<31) else u

funcs = (ulong_to_long_pack, ulong_to_long_sub, ulong_to_long2_xor)

# test
for ulong_to_long in funcs:
    print(ulong_to_long.__doc__)

    u = 2600468480
    print(u, ulong_to_long(u))

    big = 1<<31
    for u in range(big - 3, big + 3):
        print(u, ulong_to_long(u))

    print()

Выход

 using pack & unpack 
2600468480 -1694498816
2147483645 2147483645
2147483646 2147483646
2147483647 2147483647
2147483648 -2147483648
2147483649 -2147483647
2147483650 -2147483646

 using subtraction 
2600468480 -1694498816
2147483645 2147483645
2147483646 2147483646
2147483647 2147483647
2147483648 -2147483648
2147483649 -2147483647
2147483650 -2147483646

 using exclusive OR 
2600468480 -1694498816
2147483645 2147483645
2147483646 2147483646
2147483647 2147483647
2147483648 -2147483648
2147483649 -2147483647
2147483650 -2147483646

Ответ 2

Java имеет 32-битные фиксированные ширины, поэтому 155 << 24 сдвигает самый старший бит 155 (который является битом 7, считая биты с нуля, поскольку 155 больше 2 7 но менее 2 8) в знаковый бит (бит 31), и вы получите отрицательное число.

Python имеет целые числа произвольной точности, поэтому 155 << 24 численно равно положительному числу 155 × 2 24

Ответ 3

Используйте long в java, чтобы получить тот же результат

System.out.println(155L << 24);

вместо

System.out.println(155 << 24);

Длинные длины 4 байта, поэтому точность для этого контекста одинакова для целых чисел python.