Python tutorials > Core Python Fundamentals > Operators and Expressions > What are bitwise operators?

What are bitwise operators?

Understanding Bitwise Operators in Python

Bitwise operators in Python allow you to manipulate individual bits within integers. They operate on binary representations of numbers, performing logical operations on corresponding bits. This tutorial explores these operators, their functionality, and practical applications.

Introduction to Bitwise Operators

Bitwise operators work directly with the binary representation of integers. Understanding how numbers are represented in binary is crucial for effectively using these operators. Each integer is treated as a sequence of bits (0s and 1s), and the operators perform logical operations on these bits.

The Bitwise AND Operator (&)

The bitwise AND operator (&) compares corresponding bits of two operands. If both bits are 1, the resulting bit is 1; otherwise, the resulting bit is 0.

In the example, 5 (0101) AND 3 (0011) results in 1 (0001) because only the rightmost bit is 1 in both numbers.

a = 5  # Binary: 0101
b = 3  # Binary: 0011

result = a & b  # Binary: 0001 (Decimal: 1)

print(result)

The Bitwise OR Operator (|)

The bitwise OR operator (|) compares corresponding bits of two operands. If at least one bit is 1, the resulting bit is 1; otherwise, the resulting bit is 0.

In the example, 5 (0101) OR 3 (0011) results in 7 (0111) because at least one of the corresponding bits is 1.

a = 5  # Binary: 0101
b = 3  # Binary: 0011

result = a | b  # Binary: 0111 (Decimal: 7)

print(result)

The Bitwise XOR Operator (^)

The bitwise XOR (exclusive OR) operator (^) compares corresponding bits of two operands. If the bits are different, the resulting bit is 1; if they are the same, the resulting bit is 0.

In the example, 5 (0101) XOR 3 (0011) results in 6 (0110) because the differing bits result in a 1.

a = 5  # Binary: 0101
b = 3  # Binary: 0011

result = a ^ b  # Binary: 0110 (Decimal: 6)

print(result)

The Bitwise NOT Operator (~)

The bitwise NOT operator (~) is a unary operator that inverts all the bits of its operand. 0 becomes 1, and 1 becomes 0. Due to the two's complement representation of negative numbers, the result might seem unexpected.

In the example, ~5 results in -6 because of how Python (and most languages) represents negative numbers using two's complement. The NOT operator flips all bits, and then the value is interpreted as a negative number in two's complement format.

a = 5  # Binary: 0101

result = ~a  # Binary: ...11111111111111111111111111111010 (Decimal: -6)

print(result)

The Left Shift Operator (<<)

The left shift operator (<<) shifts the bits of the left operand to the left by the number of positions specified by the right operand. Zeroes are shifted in from the right.

In the example, 5 (0101) << 2 shifts the bits two places to the left, resulting in 20 (010100). Effectively, this multiplies the number by 2 raised to the power of the right operand (5 * 2^2 = 20).

a = 5  # Binary: 0101

result = a << 2  # Binary: 010100 (Decimal: 20)

print(result)

The Right Shift Operator (>>)

The right shift operator (>>) shifts the bits of the left operand to the right by the number of positions specified by the right operand. The rightmost bits are discarded. In most cases, zeroes are shifted in from the left for positive numbers. For negative numbers, the behavior (arithmetic or logical shift) might be platform-dependent.

In the example, 5 (0101) >> 2 shifts the bits two places to the right, resulting in 1 (0001). Effectively, this divides the number by 2 raised to the power of the right operand (5 // 2^2 = 1, integer division).

a = 5  # Binary: 0101

result = a >> 2  # Binary: 0001 (Decimal: 1)

print(result)

Real-Life Use Case: Setting and Clearing Flags

Bitwise operators are frequently used for manipulating flags or status bits in embedded systems or low-level programming. Each bit can represent a different attribute or state. This example demonstrates setting, checking, and clearing individual flags using bitwise OR, AND, and NOT operators.

FLAG_READ = 1  # Binary: 0001
FLAG_WRITE = 2 # Binary: 0010
FLAG_EXECUTE = 4 # Binary: 0100

permissions = 0  # Initially no permissions

# Set READ and WRITE permissions
permissions |= FLAG_READ | FLAG_WRITE  # Binary: 0011 (Decimal: 3)

# Check if READ permission is set
is_readable = permissions & FLAG_READ != 0  # True

# Clear WRITE permission
permissions &= ~FLAG_WRITE # Binary: 0001 (Decimal: 1)

print(f'Permissions: {permissions}')
print(f'Is Readable: {is_readable}')

When to use them

Use bitwise operators when:

  • You need to manipulate individual bits of data.
  • You are working with low-level programming, embedded systems, or hardware interfaces.
  • You need to optimize performance in specific scenarios where bit manipulation is more efficient.
  • You need to implement algorithms that rely on bitwise operations (e.g., hashing, compression).

Memory Footprint

Bitwise operators themselves don't directly reduce memory footprint, but using them to represent multiple flags or states within a single integer can significantly reduce the amount of memory required compared to using separate boolean variables for each flag. This is particularly relevant in memory-constrained environments.

Best Practices

  • Use meaningful constants: Define named constants for bit positions (flags) to improve code readability.
  • Comment your code: Explain the purpose of bitwise operations, especially when they are not immediately obvious.
  • Understand two's complement: Be aware of how negative numbers are represented when using the bitwise NOT operator.
  • Test thoroughly: Bitwise operations can be tricky, so test your code with various inputs to ensure correctness.

Interview Tip

When asked about bitwise operators in an interview, demonstrate your understanding by explaining their functionality, providing examples, and mentioning their common use cases (e.g., flag manipulation, low-level programming). Be prepared to discuss the bitwise NOT operator and its behavior with negative numbers. Also be prepared to discuss efficiency considerations, especially space efficiency.

Pros and Cons

Pros

  • Performance: Bitwise operations are generally very fast as they are performed at the hardware level.
  • Space efficiency: Using bitwise operations to represent multiple flags or states in a single integer can save memory.
  • Low-level control: They allow direct manipulation of data at the bit level, which is useful in certain applications.

Cons

  • Readability: Bitwise operations can make code harder to read and understand if not used carefully.
  • Complexity: Understanding the nuances of bit manipulation (especially with negative numbers) can be challenging.
  • Portability: The behavior of right shift operator (>>) with negative numbers can be platform-dependent.

FAQ

  • Why does `~5` result in `-6`?

    This is due to the two's complement representation of negative numbers. The bitwise NOT operator inverts all the bits of 5 (0101). The resulting binary value is then interpreted as a negative number in two's complement format, which is -6.

  • Are bitwise operators faster than other arithmetic operators?

    In general, bitwise operators are very fast because they are directly supported by the hardware. However, the actual performance difference may depend on the specific operation and the hardware architecture. For certain operations, compilers might optimize other arithmetic operations to be equally efficient.

  • Can bitwise operators be used with floating-point numbers?

    No, bitwise operators can only be used with integers. They operate on the binary representation of integers, which is fundamentally different from the representation of floating-point numbers.