Question
Could someone please explain how the << and >> operators are used in Go? I assume they are similar to shift operators in other programming languages, but I would like to understand what they do and how to use them correctly.
Short Answer
By the end of this page, you will understand how Go's left shift (<<) and right shift (>>) operators work, how they change integer values, when they are useful, and which common mistakes beginners should avoid when working with bitwise operations.
Concept
The << and >> operators in Go are bit shift operators.
<<shifts bits to the left>>shifts bits to the right
These operators work on integer values at the binary level.
What shifting means
Computers store integers as bits (0 and 1). For example:
5 = 00000101
If you shift 5 left by 1:
5 << 1
the bits move one position to the left:
00000101 -> 00001010
That becomes 10.
If you shift 5 right by 1:
5 >> 1
the bits move one position to the right:
00000101 -> 00000010
That becomes 2.
Why this matters
Bit shifting is useful because it is a compact way to:
- multiply or divide by powers of two
- work with binary flags
- encode or decode packed values
- manipulate low-level data such as bytes, masks, and permissions
Basic rule of thumb
For positive integers:
x << nis roughlyx * 2^nx >> nis roughlyx / 2^n
Examples:
4 << 1 // 8
4 << 2 // 16
20 >> 1 // 10
20 >> 2 // 5
Important Go-specific idea
In Go, shift operators are defined for integer types, and the amount you shift by matters.
For example:
var x uint = 3
fmt.Println(x << 2) // 12
This means: move the bits of x left by 2 positions.
Go is stricter than some languages, so type handling around shifts can matter, especially with typed vs untyped constants.
Mental Model
Think of a binary number as a row of boxes holding 0 or 1.
5 = 0 1 0 1
A left shift << pushes every bit one or more boxes to the left and fills the empty spaces on the right with 0.
A right shift >> pushes every bit to the right.
Simple analogy
Imagine digits on a conveyor belt:
- shifting left moves everything left, making the number bigger for positive values
- shifting right moves everything right, making the number smaller for positive values
Another useful mental shortcut:
<<= multiply by 2 again and again>>= divide by 2 again and again
This shortcut works well for non-negative integers.
For example:
3 << 1 // 6
3 << 2 // 12
16 >> 1 // 8
16 >> 3 // 2
Syntax and Examples
Basic syntax
value << shiftAmount
value >> shiftAmount
Example 1: Left shift
package main
import "fmt"
func main() {
x := 5
fmt.Println(x << 1) // 10
fmt.Println(x << 2) // 20
}
Explanation
5 << 1means shift the bits of5left by 1 place5 << 2means shift the bits left by 2 places- each left shift by 1 doubles the value for positive integers
Example 2: Right shift
package main
import "fmt"
func main() {
x := 20
fmt.Println(x >> 1) // 10
fmt.Println(x >> 2) // 5
}
Explanation
20 >> 1shifts right by 1, which halves the number20 >> 2shifts right by 2, which divides by 4
Example 3: Visualizing bits
package main
import "fmt"
func main() {
var x uint8 = 5
fmt.Printf("%08b\n", x) // 00000101
fmt.Printf("%08b\n", x<<1) // 00001010
fmt.Printf("%08b\n", x>>1) // 00000010
}
This example prints the binary representation so you can see the bit movement directly.
Example 4: Powers of two
package main
import "fmt"
func main() {
fmt.Println(1 << 0) // 1
fmt.Println(1 << 1) // 2
fmt.Println(1 << 2) // 4
fmt.Println(1 << 3) // 8
}
This is a common pattern when creating bit masks or powers of two.
Step by Step Execution
Consider this code:
package main
import "fmt"
func main() {
x := 6
y := x << 1
z := y >> 2
fmt.Println(x)
fmt.Println(y)
fmt.Println(z)
}
Step-by-step
1. x := 6
6 in binary is:
00000110
2. y := x << 1
Shift left by 1:
00000110 -> 00001100
That is 12.
So now:
y == 12
3. z := y >> 2
Now shift 12 right by 2:
12 in binary:
00001100
Shift right twice:
00001100 -> 00000011
That is 3.
So now:
z == 3
4. Output
The program prints:
6
12
3
Key takeaway
Shifting changes the bit pattern directly. For positive integers:
- left shift grows the value
- right shift reduces the value
Real World Use Cases
1. Working with flags and permissions
Bit shifts are often used to create individual flags.
const (
Read = 1 << 0
Write = 1 << 1
Execute = 1 << 2
)
These become:
Read = 1Write = 2Execute = 4
You can combine them with bitwise OR.
2. Building bit masks
mask := 1 << 3 // 8
This sets the 4th bit position.
Useful in:
- permissions
- feature toggles
- protocol flags
- compact state storage
3. Encoding data
Sometimes multiple small values are packed into one integer.
value := (3 << 4) | 2
This stores one value in higher bits and another in lower bits.
4. Processing bytes and binary formats
When reading file formats, network packets, or device data, shifts help extract fields.
high := b >> 4
low := b & 0x0F
5. Fast power-of-two calculations
size := 1 << 10 // 1024
This is common for buffer sizes, memory units, and capacity calculations.
Real Codebase Usage
In real Go codebases, shifts are usually not used for basic math. They are more commonly used for binary structure and intent.
Common patterns
Guarded flag definitions
const (
FlagActive = 1 << iota
FlagAdmin
FlagVerified
)
This creates powers of two automatically.
Validation before shifting
Developers often make sure values are valid before applying shifts, especially when the shift amount comes from input.
if shift < 0 {
return fmt.Errorf("invalid shift amount")
}
Packing and unpacking fields
id := uint32(15)
kind := uint32(3)
packed := (kind << 16) | id
Byte and protocol parsing
version := header >> 4
Configuration constants
const KB = 1 << 10
const MB = 1 << 20
const GB = 1 << 30
When teams avoid shifts
Developers may avoid bit shifts when plain multiplication or division is clearer:
result := n * 4
is often easier to read than:
result := n << 2
unless the code is specifically about bits, masks, bytes, or flags.
Common Mistakes
1. Thinking shifts are only for math
Shifts can multiply or divide by powers of two, but their real purpose is bit manipulation.
If your code is ordinary business logic, normal arithmetic may be clearer.
2. Forgetting that bits are being moved
Beginners may write code without understanding the binary effect.
x := 5
fmt.Println(x << 1) // 10
This is not magic multiplication. The bits are actually shifted.
3. Using shifts with the wrong type expectations
Go is strict about types. Integer type matters.
var x uint8 = 255
fmt.Println(x << 1)
This can surprise beginners because the result may not behave the way they expect if they are thinking only in terms of fixed-width storage. Always pay attention to the operand types and resulting values.
4. Confusing left shift and right shift
x := 8
fmt.Println(x << 1) // 16
fmt.Println(x >> 1) // 4
A good memory trick:
- left shift usually makes positive numbers larger
- right shift usually makes positive numbers smaller
5. Assuming right shift always means exact division
For positive powers of two, it behaves like division by 2^n.
But bit shifting is still a bit-level operation, so you should think in terms of bits, especially for signed values.
6. Writing unreadable code
This is valid:
size := 1 << 10
This may be less clear in ordinary logic:
items := count << 1
Prefer the clearest expression for the situation.
7. Forgetting operator intent in flag definitions
Broken example:
const (
A = 1
B = 2
C = 3
)
This is not suitable for unique bit flags because 3 overlaps existing bits.
Correct approach:
const (
A = 1 << 0
B = 1 << 1
C = 1 << 2
)
Comparisons
| Concept | Meaning | Best used for |
|---|---|---|
x << n | Shift bits left by n | powers of two, masks, flags |
x >> n | Shift bits right by n | extracting fields, halving powers of two |
x * 2 | Multiply arithmetically | clearer general-purpose math |
x / 2 | Divide arithmetically | clearer general-purpose math |
Shift vs multiplication/division
x << 1
is similar to:
x * 2
And:
x >> 1
is similar to:
x / 2
For many business calculations, * and / are easier to read.
Shift operators vs other bitwise operators
| Operator | Purpose |
|---|---|
& | AND: keep bits that are set in both values |
| ` | ` |
^ | XOR: set bits that differ |
&^ | AND NOT: clear selected bits |
<< | shift left |
>> | shift right |
Shifts often appear together with these operators when working with masks and flags.
Cheat Sheet
Quick syntax
x << n // shift left by n bits
x >> n // shift right by n bits
Quick rules
- Use shifts with integer values
<<moves bits left>>moves bits right- For positive integers:
x << nis likex * 2^nx >> nis likex / 2^n
1 << nis a common way to create powers of two and bit masks
Common examples
5 << 1 // 10
5 << 2 // 20
20 >> 1 // 10
20 >> 2 // 5
1 << 3 // 8
Common flag pattern
const (
Read = 1 << 0
Write = 1 << 1
Exec = 1 << 2
)
Binary printing
fmt.Printf("%08b\n", x)
Good use cases
- bit masks
- flags
- protocol fields
- byte manipulation
- powers of two
Readability tip
Use normal arithmetic when that better communicates intent.
FAQ
What does << mean in Go?
<< is the left shift operator. It moves the bits of a number to the left by a given number of positions.
What does >> mean in Go?
>> is the right shift operator. It moves the bits of a number to the right.
Is x << 1 the same as x * 2?
For positive integers, it behaves like multiplying by 2. But technically it is shifting bits, not performing general arithmetic.
Is x >> 1 the same as x / 2?
For positive integers, it behaves like dividing by 2. It is still a bitwise operation, so think carefully when signed values are involved.
Why do Go developers use 1 << n so often?
It is a clean way to create powers of two, especially for flags and bit masks.
When should I use shifts instead of multiplication or division?
Use shifts when the code is about bits, masks, flags, bytes, or binary formats. Use normal arithmetic when you want clarity in everyday calculations.
Can I use shift operators with floating-point numbers?
No. Shift operators are for integer values, not floating-point types.
Mini Project
Description
Build a small Go program that models user permissions using bit flags. This demonstrates a practical use of << by creating unique permission values and then checking whether a user has a specific permission.
Goal
Create and inspect a permission value using bit shifts and bitwise operations.
Requirements
- Define at least three permissions using
1 << n - Combine multiple permissions into one value
- Check whether a specific permission is enabled
- Print the permission value and the check results
Keep learning
Related questions
Are Go Maps Passed by Value or Reference?
Learn Go map parameter and return semantics: maps are passed by value, while copies share underlying data. Know when map pointers are useful.
Authentication for Go REST APIs: Sessions, Tokens, and Middleware
Learn practical authentication patterns for Go REST APIs, including password hashing, server sessions, bearer tokens, middleware, and security rules.
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.