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

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

В Java,

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

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

В Python:

print(155 << 24)

возвращает 2600468480

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


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

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

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


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

3 ответов


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

первый способ-использовать struct module чтобы интерпретировать число как 32-битное целое число без знака, упаковать его в байты (используя локальное соглашение endian), а затем распаковать эти байты, интерпретируя они как 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


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

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

вместо

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

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