Back to Blog
Python

Python Bitwise NOT: How ~x Works and Why It Returns Negative Numbers

python bitwise not: Understand Python's bitwise NOT operator (~), its two's complement behavior, and how to use it for bit masking and toggling without surprises.

bitwise operatorsPython integerstwo's complementbit manipulationnegative numbersPython syntax
Diagram showing the bitwise NOT operation on a binary number with negation result

python bitwise not requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

Python's bitwise NOT operator, written as ~, is a frequent source of confusion because it does not simply invert each bit in the way a beginner might expect. For an integer x, ~x evaluates to -x - 1. That means ~5 returns -6, not 10. This behavior is a direct consequence of how Python represents negative integers using two's complement with an infinite number of leading sign bits.

What Python's Bitwise NOT Actually Does

The ~ operator in Python is a unary operator that flips every bit of its operand. However, because Python integers have arbitrary precision, the result is not an unsigned bitwise complement. Instead, the operation is defined mathematically as ~x = -x - 1. Let's look at a few examples:

print(~5) # -6 print(~-5) # 4 print(~0) # -1

For 5, the binary representation is ...000101 (with infinitely many leading zeros). Flipping all bits yields ...111010, which is the two's complement representation of -6. The formula -x - 1 is the simplest way to reason about the result without writing out infinite bit strings.

Why Negative Numbers Appear: Two's Complement in Python

Python integers are objects with arbitrary precision. Positive numbers are represented with an infinite sequence of leading zeros, while negative numbers are represented in two's complement with an infinite sequence of leading ones. When you apply ~, every bit is flipped, so the infinite leading zeros of a positive number become leading ones, turning the value negative. Conversely, flipping a negative number's leading ones to zeros yields a positive result.

This is why ~5 becomes -6 rather than 10. The operation is not a simple bitwise NOT on a fixed-width binary number; it operates on the mathematical value of the integer. Understanding this representation is key to predicting the output of ~ on any integer.

Using Bitwise NOT to Clear Bits

A common practical use of ~ is to clear specific bits in an integer by combining it with the AND operator (&). For example, to clear bit 2 (value 4) from a number, you can use x & ~4:

def clear_bit(x, bit_position): mask = 1 << bit_position return x & ~mask # Clear bit 2 (value 4) from 13 (binary 1101) result = clear_bit(13, 2) # 9 (binary 1001) print(result)

The ~mask creates a value with all bits set except the one you want to clear. When ANDed with x, it forces that bit to zero while preserving all other bits. This pattern is idiomatic in low-level programming and is often used in flag manipulation or hardware register access.

Toggling Bits and Building Bit Masks

While ~ is useful for clearing bits, toggling a bit is more naturally done with XOR (^). For example, x ^ (1 << n) flips bit n. However, ~ can still be useful when you need a mask that has all bits set except a few. For instance, to create a mask that keeps only the lower 4 bits of an integer, you can use ~0xF to get the complement of that mask:

lower_four_mask = 0xF inverted_mask = ~lower_four_mask # all bits set except the lower 4 # Keep only the lower 4 bits of x result = x & lower_four_mask # Keep everything except the lower 4 bits result_high = x & inverted_mask

Note that inverted_mask is a negative number, but the AND operation works correctly because Python's bitwise operations operate on the two's complement representation. The result is still a positive integer when combined with a positive x, because the leading ones in the negative mask effectively become zeros after the AND with a positive number's leading zeros.

Common Mistakes and Unexpected Results

A frequent mistake is expecting ~5 to produce 10 (i.e., 0b101 becoming 0b010). This would only be true if Python used fixed-width unsigned integers, which it does not. Another mistake is applying ~ to booleans: ~True returns -2 because True is treated as 1. This is rarely what a developer intends and can lead to subtle bugs.

Also, ~ does not work on floats. Attempting ~3.14 raises a TypeError. If you need to perform bitwise operations on floating-point data, you must first convert to an integer type, which may lose precision. Always verify that the operand is an integer before using ~ in production code.

Performance and Readability Considerations

Bitwise NOT is a single CPU instruction and is extremely fast. In performance-sensitive code, using ~ to clear bits is efficient. However, readability can suffer if the reader is not familiar with two's complement behavior. In many high-level applications, using explicit masks with & and | is clearer and less error-prone. For example, x & ~mask is concise but may be less obvious than x & (0xFF ^ mask) if the mask is small.

When performance is not a bottleneck, prioritize clarity. If you are writing code that will be maintained by others, consider adding a comment explaining what ~mask accomplishes. The performance benefit of ~ is negligible compared to the cost of a misinterpreted expression.

Handling Non-Integer Types and Edge Cases

The ~ operator is defined only for integers in Python. It does not work on floats, strings, or custom objects unless they implement the __invert__ method. For custom classes, you can override __invert__ to define your own behavior, but this is rare and should be done with care.

In the standard library, ~ is also used with enum.IntFlag members to invert flag sets. For example, if you have a flag READ, then ~READ represents all flags except READ. This is a natural extension of the bitwise NOT semantics to enumeration flags.

When working with large integers, the infinite two's complement representation means that ~x always produces a negative number for positive x, and vice versa. This property can be used to check the sign of an integer without explicit comparison: x < 0 is equivalent to ~x >= 0, though the comparison is more readable. Understanding these edge cases helps you avoid surprising behavior when applying ~ in complex expressions.

python bitwise not: Practical Usage and Code Examples | RYUSLOG DEV