Skip to content
VLSI Mentor

Python Part I · Module 3 · Operators and Expressions

Bitwise Operators: & | ^ ~ << >>

Every operator so far has treated a number as a single quantity. These six treat it as a row of bits and work on each position independently, which is how you test whether a flag is set, extract a field from a packed value, or move a value into position. For a semiconductor engineer this is not an exotic corner of the language — it is the operator family that maps most directly onto the register and bus work in Part II, so it gets more care here than a general Python course would give it. The chapter builds each operation from an aligned binary example before naming the symbol, and it spends real time on one distinction that catches almost every hardware engineer exactly once: a Python integer does not have a width, and that changes what one of these operators does.

Foundation15 min readPythonPart IFundamentalsBitwiseMasks

Part I · Module 3 · Chapter 3.7 · Bitwise Operators: & | ^ ~ << >>

1. Writing a Number in Binary

Before the operators, two pieces of notation.

0b in front of a number means the digits are binary.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(0b1010)
print(0b1111)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
10
15

Both lines printed an ordinary decimal number, because that is what they are. 0b1010 is not a special kind of value — it is the integer ten, written in a way that makes its bit pattern visible in the source.

bin() goes the other way. It is a name Python provides, used like print and type: give it an integer and it hands back a piece of text showing the binary form.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(bin(10))
print(bin(255))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
0b1010
0b11111111

Notice the result is text — the 0b prefix is part of it. That is exactly what you want for looking at a result.

2. Bitwise AND — &

Line up two numbers in binary and compare each position. A result bit is 1 only when both input bits are 1.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    1100      12
&   1010      10
--------
    1000       8

Position by position: 1 and 1 gives 1; 1 and 0 gives 0; 0 and 1 gives 0; 0 and 0 gives 0.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
a = 0b1100
b = 0b1010

print(a & b)
print(bin(a & b))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
8
0b1000

What it is for: testing and extracting

Because & gives 1 only where both have 1, an all-ones pattern lets selected positions through and blocks the rest. Such a pattern is called a mask.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
value = 0b10110110
mask  = 0b00001111

print(bin(value & mask))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
0b110

The mask has 1s in the low four positions and 0s above. Everywhere the mask is 0 the result is forced to 0; everywhere it is 1 the original bit survives. So the answer is the bottom four bits of value — which were 0110, printed as 0b110 because leading zeros are not shown.

The same idea tests a single bit:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
flags = 0b1010

print(flags & 0b0010)
print(flags & 0b0001)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2
0

A non-zero answer means that bit was set; zero means it was not.

3. Bitwise OR — |

A result bit is 1 when either input bit is 1.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    1100      12
|   1010      10
--------
    1110      14
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
a = 0b1100
b = 0b1010

print(a | b)
print(bin(a | b))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
14
0b1110

What it is for: setting bits

| turns positions on without disturbing the others:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
flags = 0b1010

print(bin(flags | 0b0001))
print(bin(flags | 0b0100))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
0b1011
0b1110

The first set the lowest bit; the second set the third. In both cases every other position kept its original value.

4. Bitwise XOR — ^

A result bit is 1 when the two input bits are different.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    1100      12
^   1010      10
--------
    0110       6
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
a = 0b1100
b = 0b1010

print(a ^ b)
print(bin(a ^ b))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
6
0b110

What it is for: flipping, and detecting difference

XOR against a 1 flips that position; against a 0 it leaves it alone:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
flags = 0b1010
print(bin(flags ^ 0b1111))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
0b101

Every one of the low four bits was flipped.

It also answers are these different? — XOR of a value with itself is always zero:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(0b1010 ^ 0b1010)
print(0b1010 ^ 0b1011)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
0
1

Zero means identical; anything non-zero marks exactly which positions differ.

5. Shifting — << and >>

<< moves every bit left by the given number of positions, filling with zeros on the right.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    0011        3
<< 2
    1100       12
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(3 << 2)
print(bin(3 << 2))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
12
0b1100

>> moves bits right, discarding what falls off the end.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    1100       12
>> 2
    0011        3
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(12 >> 2)
print(bin(12 >> 2))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
3
0b11

For non-negative numbers, once the bit movement is clear, the arithmetic effect is easy to see: shifting left by n multiplies by 2 to the power n, and shifting right divides and rounds down.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(3 << 4)
print(3 * (2 ** 4))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
48
48

What it is for: building a mask for one position

The most common use you will meet:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(bin(1 << 0))
print(bin(1 << 3))
print(bin(1 << 7))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
0b1
0b1000
0b10000000

1 << n produces a value with exactly bit n set, which is how a single-bit mask is built without writing the zeros out by hand. Combined with & from §2:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
status = 0b10110110
print(status & (1 << 4))
print(status & (1 << 3))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
16
0

Bit 4 is set; bit 3 is not. The brackets around 1 << 4 are not required, but they make the intent obvious — Chapter 3.8 argues that case properly.

6. A Python Integer Has No Width

This section is the reason the chapter needs care, and it is where a hardware background actively misleads.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
value = 0b1111
print(value)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
15

0b1111 does not create a four-bit value. It creates the integer fifteen, written in binary. Python does not remember that you typed four digits, and there is no width attached to the result.

That is a genuine difference from RTL. In SystemVerilog, 4'b1111 is a four-bit vector and the width is part of the type — it governs what happens on overflow and inversion. Python has nothing equivalent. Chapter 2.6 §4 already noted that Python integers have no size limit; this is the same fact showing up in bit operations.

7. Bitwise NOT — ~

Here is where the missing width shows.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(~3)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-4

Not 12, which is what "flip the four bits I typed" would give. Python had no four bits to flip.

The behaviour is consistent and easy to state:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(~0)
print(~1)
print(~3)
print(~255)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-1
-2
-4
-256

Every answer follows the same rule: ~x produces -(x + 1).

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
x = 3
print(~x)
print(-(x + 1))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-4
-4

The reason is that Python's integers behave as though they have an unlimited run of leading sign bits, so inverting produces a negative number rather than a bounded pattern. You do not need the representation to use the rule.

8. Inverting Within a Fixed Width

To invert and keep only a chosen number of bits, invert and then mask.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
value = 0b1111
mask  = 0b1111

print(~value)
print(~value & mask)
print(bin(~value & mask))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
-16
0
0b0

Two steps. ~value produced -16, which is not what you wanted on its own. Then & mask kept only the low four positions — and since inverting all-ones within four bits gives all-zeros, the answer is 0.

A case where the result is more visible:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
value = 0b1010
mask  = 0b1111

print(bin(~value & mask))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
0b101

1010 inverted within four bits is 0101, printed as 0b101.

The mask does exactly what §2 described: 0b1111 has ones in the four positions you care about, so only those four survive. Written in hex it is the same value and slightly easier to read at larger widths:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(bin(~0b1010 & 0xF))
print(bin(~0b10101010 & 0xFF))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
0b101
0b1010101

0xF is four bits of ones; 0xFF is eight.

9. These Are Not and, or and not

Chapter 3.5 §6 made this point from the other side; here it is from this one.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(6 & 3)
print(6 and 3)
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
2
3

& compared the bits of six and three: 0110 & 0011 is 0010, which is 2. and asked a logical question and handed back one of the values.

They are different operators doing different jobs. Words for logic, symbols for bits.

10. Summary of the Six

OperatorEach result bitCommon use
&1 when both are 1test a bit, extract a field with a mask
|1 when either is 1set bits
^1 when they differflip bits, detect difference
~inverts — but no width, so ~x is -(x + 1)rarely alone; pair with a mask
<<shifts left, zero-filledbuild 1 << n, scale up
>>shifts right, discardingextract a field after masking, scale down

11. Predict, Run, Observe

Predict each — in binary where the output is shown that way.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
a = 0b1100
b = 0b1010

print(a & b)
print(a | b)
print(a ^ b)
print(bin(a & b))
print(bin(a | b))
print(bin(a ^ b))
print(a << 1)
print(a >> 2)
print(~a)
print(bin(~a & 0b1111))
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
8
14
6
0b1000
0b1110
0b110
24
3
-13
0b11

The last two are the check. ~a gave -13 — following -(x + 1) with a being 12 — and masking to four bits gave 0b11, because 1100 inverted within four bits is 0011.

12. Exercise

Exercise 1 — reason it out first. Given x = 0b1101 and y = 0b0111, write out the aligned column working for x & y, x | y and x ^ y the way §2 does. Then check all three with bin().

Exercise 2 — extract a field. Given status = 0b11010110, use a mask to print just the low four bits in binary. Then use a different mask to print just the low two.

Exercise 3 — test a bit. Using 1 << n and &, print whether bit 2 and bit 5 of 0b00100100 are set. A non-zero answer means set.

Exercise 4 — the width trap. Predict both, then run:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(bin(~0b1010))
print(bin(~0b1010 & 0xF))

Explain in one sentence why the first is not a four-bit answer.

Exercise 5 — the silent typo. Predict both, then run:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
print(2 ** 5)
print(2 ^ 5)

Neither fails. Say in one sentence which one you almost certainly meant and how you would spot the mistake in a review.

Exercise 6 — set without disturbing. Given flags = 0b1001, print the binary result of setting bit 1 without changing any other position. Confirm bits 0 and 3 are still set.

13. Summary

  • 0b writes a number in binary, 0x in hexadecimal, and bin() shows an integer's binary form as text. All are spellings of ordinary integers.
  • & gives 1 only where both are 1 — used with a mask to test a bit or extract a field.
  • | gives 1 where either is 1 — used to set bits without disturbing others.
  • ^ gives 1 where they differ — used to flip bits and to detect difference. It is not exponentiation.
  • << and >> move bits, and 1 << n is the standard way to build a single-bit mask.
  • A Python integer has no width. 0b1111 is the number 15, not a 4-bit vector — no overflow, no wraparound.
  • ~x is -(x + 1), not "flip the bits you typed". For fixed-width inversion, mask afterwards: ~value & 0xF.
  • & and | are not and and or. On integers they give different answers.

One chapter remains, and it settles a question that has been deferred since 3.1: when several operators appear in one expression, which happens first?

Where this fits

Part of the Python curriculum.