Побитовая операция унарная ~ (инвертированная)

Я немного смущен оператором ~. Код идет ниже:

a = 1
~a  #-2
b = 15
~b  #-16

Как работает ~?

Я думал, ~a будет примерно таким:

0001 = a
1110 = ~a 

почему бы и нет?

Ответ 1

Вы совершенно правы. Это артефакт двух дополнений целочисленного представления.

В 16 бит 1 представляется как 0000 0000 0000 0001. Перевернутый, вы получаете 1111 1111 1111 1110, который равен -2. Аналогично, 15 0000 0000 0000 1111. Перевернутый, вы получаете 1111 1111 1111 0000, который равен -16.

В общем случае ~n = -n - 1

Ответ 2

Оператор '~' определяется как: "Побитовая инверсия x определяется как - (x + 1), она применима только к целым числам". Python Doc - 5.5

Важная часть этого предложения состоит в том, что это связано с "целыми числами" (также называемыми целыми числами). Ваш пример представляет 4-битное число.

'0001' = 1 

Integer диапазон 4-битного числа равен -8.0..7. С другой стороны, вы можете использовать "целые числа без знака", которые не включают отрицательное число, а диапазон для вашего 4-битного номера будет "0..15".

Поскольку Python работает с целыми числами, поведение, о котором вы описали, ожидается. Целые числа представлены с использованием двух дополнений. В случае 4-битного номера это выглядит следующим образом.

 7 = '0111'
 0 = '0000'
-1 = '1111'
-8 = '1000'

Python использует 32-битное целочисленное представление, если у вас 32-разрядная ОС. Вы можете проверить наибольшее целое число с помощью:

sys.maxint # (2^31)-1 for my system

Если вы хотите, чтобы целое число без знака вернуло вам 4-битное число, вы должны замаскировать.

'0001' = a   # unsigned '1' / integer '1'
'1110' = ~a  # unsigned '14' / integer -2

(~a & 0xF) # returns 14

Если вы хотите получить беззнаковый 8-разрядный диапазон номеров (0..255), просто используйте:

(~a & 0xFF) # returns 254

Ответ 3

Похоже, я нашел более простое решение, которое делает то, что вам нужно:

uint8: x ^ 0xFF<br>
uint16: x ^ 0xFFFF<br>
uint32: x ^ 0xFFFFFFFF<br>
uint64: x ^ 0xFFFFFFFFFFFFFFFF

Ответ 4

Вы также можете использовать unsigned ints (например, из пакета numpy) для достижения ожидаемого поведения.

>>> import numpy as np
>>> bin( ~ np.uint8(1))
'0b11111110'