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.`x` | 0 | 1 | 0 | 0 | 0 | 0 | 0 |

`~x` | 1 | 0 | 1 | 1 | 1 | 1 | 1 |

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` |

`-1` | `0` |

`-3` | `2` |

`-9` | `8` |

`-11` | `10` |

`-256` | `255` |

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.

Operator | Name | Description | Example |
---|---|---|---|

`x = 4, y = 5` | |||

& | Bitwise AND | Performs logical AND on a bit-by-bit basis | `x & y` |

| | Bitwise OR | Performs logical OR operation on a bit-by-bit basis | `x | y` |

~ | Bitwise NOT | Performs 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` |

^ | Bitwise XOR | Performs logical “exclusive or” operation on a bit-by-bit basis | `x ^ y` |

>> | Bitwise right shift | Shifts binary of left operand to the right by the number of positions specified in right operand | `x >> 2` |

<< | Bitwise left shift | Shifts binary of left operand to the left by the number of positions specified in right operand | `x << 2` |

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

Bitwise Operator | Magic “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