# Overview of Python’s Bitwise Operators
Bitwise operators look virtually the same across different programming languages:

|Operator	| Example	| Meaning|
|:---|:---|:---|
|&	|a & b	|Bitwise AND|
|\|	|a \| b	|Bitwise OR|
|^	|a ^ b	|Bitwise XOR (exclusive OR)|
|~	|~a	|Bitwise NOT|
|<<	|a << n	|Bitwise left shift|
|>>	|a >> n	|Bitwise right shift|

All binary bitwise operators have a corresponding **compound operator** that performs an augmented assignment:

|Operator	|Example	|Equivalent to|
|---|---|---|
|&=	|a &= b	|a = a & b|
|\|=	|a \|= b	|a = a \| b|
|^=	|a ^= b	|a = a ^ b|
|<<=	|a <<= n	|a = a << n|
|>>=	|a >>= n	|a = a >> n|


# Why Use Binary?

Most modern civilizations use **positional notation(进位制)**, which is efficient, flexible, and well suited for doing arithmetic.

- base-two numeral system (binary system), for example 0x11101
- base-eight numeral system, 
- base-ten numeral system， also known as the decimal system
- base-sixteen numeral system


Pros and Cons of Binary system vs decimal system

- The binary system requires more storage space than the decimal system but is much less complicated to implement in hardware
- the binary system is perfect for electronic devices, which translate digits into different voltage levels.
- By employing only two states, you make the system more reliable and resistant to noise



```python
len("€uro".encode("utf-8"))
```




    6




```python
(42).bit_length()
```




    6




```python
for char in "€uro":
    print(char, char.encode("utf-8"), len(char.encode("utf-8")))
```

    € b'\xe2\x82\xac' 3
    u b'u' 1
    r b'r' 1
    o b'o' 1


# Bitwise Logical Operators

## Evaluating Boolean Expressions With Bitwise Operators

Although this expression is syntactically correct, there are a few problems with it. 
1. it’s arguably less readable
2. it doesn’t work as expected for all groups of data


```python
# The expression made of the bitwise operators evaluates to True, while the same expression built 
# from the logical operators evaluates to False. That’s because bitwise operators take precedence 
# over the comparison operators, changing how the whole expression is interpreted:
# so the bit expression is interpreted as: age >= (18 & ~is_self_excluded)
age = 18
is_self_excluded = True
print(age >= 18 & ~is_self_excluded)  # Bitwise logical operators

print(age >= 18 and not is_self_excluded)  # Logical operators
```

    True
    False



```python
# It’s as if someone put implicit parentheses around the wrong operands. To fix this, 
# you can put explicit parentheses, which will enforce the correct order of evaluation:
(age >= 18) & ~is_self_excluded
```




    0



However, you no longer get a Boolean result. Python bitwise operators were designed primarily to work with integers, so their operands automatically get casted if needed. 

Unlike their logical counterparts, bitwise operators are evaluated eagerly
1. short-circuit evaluation of Boolean expressions: Expressions using logical operators are evaluated lazily from left to right, it will stop as soon as the result of the entire expression is known

2. For bitwise operators, even though knowing the left operand is sufficient to determine the value of the entire expression, all operands are always evaluated unconditionally.

### Bitwise AND

1. The bitwise AND operator (&) performs **logical conjunction** on the corresponding bits of its operands. The resulting bit pattern is an intersection of the operator’s arguments
2. Arithmetically, this is equivalent to a **product** of two bit values
\begin{aligned}
(a \& b)_i = a_i \times b_i
\end{aligned}
3. Notice that when operands have unequal bit-lengths, the shorter one is automatically padded with zeros to the left.
4. Alternatively, you can take the **minimum** of the two bits in each pair.


```python
a = 156
b = 52
c = a & b
print(f"a {bin(a)}, {a}")
print(f"b {bin(b)}, {b}")
print(f"c {bin(c)}, {c}")
```

    a 0b10011100, 156
    b 0b110100, 52
    c 0b10100, 20


### Bitwise OR

1. The bitwise OR operator (|) performs **logical disjunction**. For each corresponding pair of bits, it returns a one if at least one of them is switched on.
2. The resulting bit pattern is a **union** of the operator’s arguments.
3. The arithmetic behind it is a combination of a **sum** and a **product** of the bit values.
\begin{equation}
(a | b)_i = a_i + b_i - (a_i \times b_i)
\end{equation}



```python
c = a | b
print(f"a {bin(a)}, {a}")
print(f"b {bin(b)}, {b}")
print(f"c {bin(c)}, {c}")
```

    a 0b10011100, 156
    b 0b110100, 52
    c 0b10111100, 188


### Bitwise XOR
1. Unlike bitwise AND, OR, and NOT, the bitwise XOR operator (^) doesn’t have a logical counterpart in Python.
2. The name XOR stands for “exclusive or” since it performs exclusive disjunction on the bit pairs. In other words, every bit pair must contain opposing bit values to produce a one
3. Similarly to the bitwise OR operator, the arithmetic of XOR involves a sum. However, while the bitwise OR clamps values at one, the XOR operator wraps them around with a sum modulo two
\begin{equation}
(a^b)_i = (a_i + b_i) \; mod \; 2
\end{equation}


```python
def xor(a, b):
    return (a and not b) or (not a and b)
```


```python
c = a ^ b
print(f"a {bin(a)}, {a}")
print(f"b {bin(b)}, {b}")
print(f"c {bin(c)}, {c}")
```

    a 0b10011100, 156
    b 0b110100, 52
    c 0b10101000, 168


### Bitwise NOT

1. It performs **logical negation** on a given number by flipping all of its bits.
2. It can be expressed arithmetically as the subtraction of individual bit values from one
\begin{equation}
\sim a_i = 1 - a_i
\end{equation}


```python
c = ~ a
print(f"a {bin(a)}, {a}")
print(f"c {bin(c)}, {c}")
```

    a 0b10011100, 156
    c -0b10011101, -157


### Bitwise Shift Operators

1. In general, shifting bits to the left corresponds to multiplying the number by a power of two, with an exponent equal to the number of places shifted
\begin{equation}
a << n = a \times 2^n
\end{equation}
2. The left shift used to be a popular optimization technique because bit shifting is a single instruction and is cheaper to calculate than exponent or product.


```python
a = 56
print (f"{a << 1} == {a * 2^1}")
print (f"{a << 2} == {a * 2^2}")
print (f"{a << 3} == {a * 2^3}")
```

    112 == 113
    224 == 114
    448 == 115



```python
print(f"{39 << 3}")
print(f"{(39 << 3) & 0xFF}")
```

    312
    56


3. the right shift operator automatically floors the result. It’s virtually the same as a floor division by a power of two
\begin{equation}
a >> n = \lfloor \frac{a}{2^n} \rfloor
\end{equation}


```python
a = 157
print(f"a: {a}")
print(f"a >> 1: {a >> 1}")
print(f"a >> 2: {a >> 2}")
print(f"a >> 2: {a >> 2}")
```

    a: 157
    a >> 1: 78
    a >> 2: 39
    a >> 2: 39


In the following transformations: 
1. The bitwise right shift operator and the floor division operator both work the same way, even for negative numbers. 
2. However, the floor division lets you choose any divisor and not just a power of two. 
3. Using the bitwise right shift was a common way of improving the performance of some arithmetic divisions.


```python
print(f"5 >> 1: {5 >> 1}")  # Bitwise right shift
print(f"5 // 2: {5 // 2}")  # Floor division (integer division)
print(f"5 / 2 : {5 / 2 }")  # Floating-point division
```

    5 >> 1: 2
    5 // 2: 2
    5 / 2 : 2.5



```python

```

# Reference

1. [Bitwise Operators in Python](https://realpython.com/python-bitwise-operators/)


```python

```

a537e3107edc0dd3b328d5ed9f586e0aa01c7eef
ipynb
Jupyter Notebook
Python/Bit_Operation.ipynb
bingrao/notebook
4bd74a09ffe86164e4bd318b25480c9ca0c6a462
MIT
Python/Bit_Operation.ipynb
bingrao/notebook
4bd74a09ffe86164e4bd318b25480c9ca0c6a462
MIT
Python/Bit_Operation.ipynb
bingrao/notebook
4bd74a09ffe86164e4bd318b25480c9ca0c6a462
MIT
Qwen/Qwen-72B
1. YES
2. YES