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

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

В Java

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

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

В Python:

print(155 << 24)

Возвращает 2600468480

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


РЕДАКТИРОВАТЬ: я пытаюсь создать функцию в Python, чтобы повторить, как работает левый оператор сдвига в Java. Что-то вроде:

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

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


РЕДАКТИРОВАТЬ 2: Несколько часов спустя я решил, что, вероятно, не самая лучшая идея использовать Python для этой работы, и примет участие в приложении Java и использует его в качестве микро-службы для существующего приложения Python.

3 ответа

Решение

Вот 3 различных способа преобразовать целое число 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

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

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

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

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

вместо

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

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

Другие вопросы по тегам