Python Bitwise NOT Operator

Python’s bitwise NOT operator ~x inverts each bit from the binary representation of integer x so that 0 becomes 1 and 1 becomes 0. This is semantically the same as calculating ~x == -x-1. For example, the bitwise NOT expression ~0 becomes -1, ~9 becomes -10, and ~32 becomes -33.

As you go over the article, you can watch my explainer video here:

In this example, you apply the bitwise NOT operator to integer 32:

>>> ~32
-33

The expression ~32 operates on the bit representations "0100000" (decimal 32) and performs bitwise NOT resulting in binary "1011111". This corresponds to the negative decimal number -33.

x0100000
~x1011111

How do you transform the binary “1011111” to a decimal number again? By using the following steps:

  • Flip each bit back to 0100000.
  • Get the corresponding decimal number 32.
  • Increase it by one to 33.
  • Prefix it with the negative symbol -33.

To understand this inverse method from a negative binary to an integer, you need to learn some background first. But don’t worry, it’s just a couple of minutes! 🐍

Representing Negative Integers in Binaries

Python uses so-called complementary binaries to represent negative integers. The first bit of a complementary binary is the sign (0: positive, 1: negative). All remaining bits encode the number. You write a negative number -x as the bit pattern for (x-1) and flip all bits from 1 to 0 and from 0 to 1 (complement).

Here are two simple examples:

  • To represent x = -1 using 8 bits you first calculate (1-1) = 0 and then flip all bits to calculate "11111111".
  • To represent x = -10 using 8 bits you first calculate (10-1) = 9 which is "00001001" in binary format. Then, you complement all bits to determine the negative (complementary) binary "11110110".

💡 In fact, Python uses signed integers for its bitwise operators. You may ask: what are signed integers?

  • A signed integer, for example using 32 bits, encodes an integer in the range [-2147483648 to 2147483647].
  • An unsigned integer encodes a positive integer in the range [0 to 4294967295]. The signed integer is represented in twos complement notation.

Python Bitwise NOT Operator Example

Here’s the result of the bitwise NOT operator ~x when applied to a couple of example integer operands x:

x (int)x (binary)~x (binary)~x (int)
0'00''11'-1
1'01''10'-2
3'011''100'-4
9'01001''10110'-10
11'01011''10100'-12
256'0100000000''1011111111'-257

You can see those examples in the following Python script:

>>> ~0
-1
>>> ~1
-2
>>> ~3
-4
>>> ~9
-10
>>> ~11
-12
>>> ~256
-257

Let’s use this knowledge in a couple of examples to showcase the working of the bitwise NOT operator on negative integers:

Python Bitwise NOT Examples on Negative Integers

Here’s the result of the bitwise NOT operator ~x when applied to a negative integer operand x:

x (int)~x (int)
-0-1
-10
-32
-98
-1110
-256255

You can see those examples in the following script:

>>> ~-0
-1
>>> ~-1
0
>>> ~-3
2
>>> ~-9
8
>>> ~-11
10
>>> ~-256
255

Python Bitwise NOT Overloading

You can define your own bitwise NOT operator on a custom class by overloading the __invert__ method (dunder method, magic method) with a reference to self as an argument. This allows the expression ~x on your custom objects without raising an error.

Here’s an example:

class Data:
    def __init__(self, data):
        self.data = data

    def __invert__(self):
        return Data(~self.data)


x = Data(3)

res = ~x
print(res.data)
# -4

Note: if you forget to overwrite the __invert__ method and still try to use the expression ~x, Python will raise a TypeError: bad operand type for unary ~. You can fix it by defining the dunder method __invert__(self) in your class definition.

class Data:
    def __init__(self, data):
        self.data = data


x = Data(3)

res = ~x
print(res.data)

Output:

Traceback (most recent call last):
   File "C:\Users\xcent\Desktop\code.py", line 8, in 
     res = ~x
 TypeError: bad operand type for unary ~: 'Data'

To fix this TypeError, simply define the __invert__ method as shown in the previous working example.

Bitwise Operators

Bitwise operators perform operations on the binary (bit) representation of integers. The following table gives a short overview of all existing bitwise operators. Note that we also provide the binary representation 100 for the decimal integer 4, and 101 for the decimal integer 5 as a comment in the right column.

OperatorNameDescriptionExample
x = 4, y = 5
&Bitwise ANDPerforms logical AND on a bit-by-bit basisx & y
# b100 & b101 == b100 == 4
|Bitwise ORPerforms logical OR operation on a bit-by-bit basisx | y
# b100 | b101 == b101 == 5
~Bitwise NOTPerforms logical NOT on a bit-by-bit basis, inverting each bit so that 0 becomes 1 and 1 becomes 0. Same as -x-1.~x
# -4-1 == -5
^Bitwise XORPerforms logical “exclusive or” operation on a bit-by-bit basisx ^ y
# b100 ^ b101 == b001 == 1
>>Bitwise right shiftShifts binary of left operand to the right by the number of positions specified in right operandx >> 2
# b100 == b010 == b001 == 1
<<Bitwise left shiftShifts binary of left operand to the left by the number of positions specified in right operandx << 2
# b100 == b1000 == b10000 == 16

Here’s a short overview of the Bitwise operators’ magic methods:

Bitwise OperatorMagic “Dunder” Method
&__and__(self, other)
|__or__(self, other)
^__xor__(self, other)
~__invert__(self)
<<__lshift__(self, other)
>>__rshift__(self, other)

Here’s an example of how to accomplish these bitwise operators on a custom class Data. We marked this respective operator in the code:

class Data:

    def __init__(self, data):
        self.data = data

    def __and__(self, other):
        return Data(self.data & other.data)

    def __or__(self, other):
        return Data(self.data | other.data)
    
    def __xor__(self, other):
        return Data(self.data ^ other.data)
    
    def __invert__(self):
        return Data(~self.data)
    
    def __lshift__(self, other):
        return Data(self.data << other.data)
    
    def __rshift__(self, other):
        return Data(self.data >> other.data)


x = 2
y = 3
print('Operands: \n', 'x =', x, '\n', 'y =', y)
print()
print('Bitwise AND: ', x & y)
print('Bitwise OR: ', x | y)
print('Bitwise XOR: ', x ^ y)
print('Bitwise NOT: ', ~x)
print('Bitwise LEFT-SHIFT: ', x << y)
print('Bitwise RIGHT-SHIFT: ', x >> y)

The output is:

Operands: 
  x = 2 
  y = 3

 Bitwise AND:  2
 Bitwise OR:  3
 Bitwise XOR:  1
 Bitwise NOT:  -3
 Bitwise LEFT-SHIFT:  16
 Bitwise RIGHT-SHIFT:  0