Bitwise Operations Guide
This guide provides a comprehensive introduction to bitwise operations in Harneet, covering concepts, practical applications, and performance considerations.
Understanding Binary Representation
Before diving into bitwise operations, it's essential to understand how integers are represented in binary.
Binary Basics
| Binary Basics |
|---|
| // Decimal to Binary Examples
12 → 1100 (binary)
10 → 1010 (binary)
5 → 0101 (binary)
0 → 0000 (binary)
|
Bit Positions
| Bit Positions |
|---|
| Bit Position: 7 6 5 4 3 2 1 0
Binary: 1 1 0 0 1 0 1 0
Decimal Value: 128+64+0+0+8+0+2+0 = 202
|
Bitwise Operators
Bitwise AND (&)
The AND operator returns 1 only when both bits are 1.
| Bitwise AND |
|---|
| package main
import fmt
var a int = 12 // 1100
var b int = 10 // 1010
var result int = a & b // 1000 = 8
fmt.Printf("%d & %d = %d\n", a, b, result)
|
Truth Table:
| A | B | A & B
--|---|------
0 | 0 | 0
0 | 1 | 0
1 | 0 | 0
1 | 1 | 1
|
Common Uses: - Masking bits - Checking if specific bits are set - Clearing bits
Bitwise OR (|)
The OR operator returns 1 when at least one bit is 1.
| Bitwise OR |
|---|
| package main
import fmt
var a int = 12 // 1100
var b int = 10 // 1010
var result int = a | b // 1110 = 14
fmt.Printf("%d | %d = %d\n", a, b, result)
|
Truth Table:
| A | B | A | B
--|---|------
0 | 0 | 0
0 | 1 | 1
1 | 0 | 1
1 | 1 | 1
|
Common Uses: - Setting bits - Combining flags - Creating bitmasks
Bitwise XOR (^)
The XOR operator returns 1 when bits are different.
| Bitwise XOR |
|---|
| package main
import fmt
var a int = 12 // 1100
var b int = 10 // 1010
var result int = a ^ b // 0110 = 6
fmt.Printf("%d ^ %d = %d\n", a, b, result)
|
Truth Table:
| A | B | A ^ B
--|---|------
0 | 0 | 0
0 | 1 | 1
1 | 0 | 1
1 | 1 | 0
|
Common Uses: - Toggling bits - Simple encryption - Swapping variables without temporary storage
Bitwise NOT (~)
The NOT operator inverts all bits (1 becomes 0, 0 becomes 1).
| Bitwise NOT |
|---|
| package main
import fmt
var a int = 5 // 0101
var result int = ~a // 1010... (depends on integer size)
fmt.Printf("~%d = %d\n", a, result)
|
Note: The result depends on the integer size and sign representation.
Left Shift (<<)
Shifts bits to the left, filling with zeros from the right.
| Left Shift |
|---|
| package main
import fmt
var a int = 5 // 0101
var result int = a << 2 // 010100 = 20
fmt.Printf("%d << 2 = %d\n", a, result)
|
Effect: Equivalent to multiplying by 2^n where n is the shift amount.
Right Shift (>>)
Shifts bits to the right, discarding bits that fall off.
| Right Shift |
|---|
| package main
import fmt
var a int = 20 // 10100
var result int = a >> 2 // 101 = 5
fmt.Printf("%d >> 2 = %d\n", a, result)
|
Effect: Equivalent to dividing by 2^n where n is the shift amount (for positive numbers).
Practical Applications
1. Flag Management
Bitwise operations are perfect for managing multiple boolean flags efficiently.
| Flag Management |
|---|
| package main
import fmt
// Define flag constants
var READ_FLAG int = 1 // 001
var WRITE_FLAG int = 2 // 010
var EXEC_FLAG int = 4 // 100
// Set permissions
var permissions int = READ_FLAG | WRITE_FLAG
fmt.Printf("Permissions: %d\n", permissions) // 3
// Check if has read permission
if (permissions & READ_FLAG) != 0 {
fmt.Println("Has read permission")
}
// Add execute permission
permissions |= EXEC_FLAG
fmt.Printf("After adding execute: %d\n", permissions) // 7
// Remove write permission
permissions &= ~WRITE_FLAG
fmt.Printf("After removing write: %d\n", permissions) // 5
// Toggle read permission
permissions ^= READ_FLAG
fmt.Printf("After toggling read: %d\n", permissions) // 4
|
2. Bit Manipulation Tricks
Check if Number is Even/Odd
| Even/Odd Check |
|---|
| package main
import fmt
function isEven(n int) bool {
return (n & 1) == 0
}
var num int = 42
if isEven(num) {
fmt.Printf("%d is even\n", num)
}
|
Check if Number is Power of 2
| Power of Two Check |
|---|
| package main
import fmt
function isPowerOfTwo(n int) bool {
return n > 0 and (n & (n - 1)) == 0
}
var numbers = [1, 2, 3, 4, 8, 15, 16]
for var i = 0; i < len(numbers); i = i + 1 {
var num int = numbers[i]
fmt.Printf("%d is power of 2: %t\n", num, isPowerOfTwo(num))
}
|
Swap Two Numbers Without Temporary Variable
| Swap Without Temp |
|---|
| package main
import fmt
var a int = 25
var b int = 17
fmt.Printf("Before: a=%d, b=%d\n", a, b)
a ^= b
b ^= a
a ^= b
fmt.Printf("After: a=%d, b=%d\n", a, b)
|
Count Set Bits (Population Count)
| Count Set Bits |
|---|
| package main
import fmt
function countSetBits(n int) int {
var count int = 0
for n > 0 {
count += n & 1
n >>= 1
}
return count
}
// Optimized version (Brian Kernighan's algorithm)
function countSetBitsFast(n int) int {
var count int = 0
for n > 0 {
n &= n - 1 // Clear the lowest set bit
count = count + 1
}
return count
}
var num int = 42 // 101010 in binary
fmt.Printf("Set bits in %d: %d\n", num, countSetBits(num))
|
Fast Multiplication/Division by Powers of 2
| Fast Multiply/Divide by 2^n |
|---|
| // Instead of: result = value * 8
var result int = value << 3 // Faster
// Instead of: result = value / 4
var result2 int = value >> 2 // Faster (for positive numbers)
// Modulo with powers of 2
// Instead of: remainder = value % 8
var remainder int = value & 7 // Faster (8-1 = 7)
|
Absolute Value Without Branching
| Absolute Value Without Branching |
|---|
| package main
import fmt
function abs(n int) int {
var mask int = n >> 31 // Arithmetic right shift
return (n ^ mask) - mask
}
var values = [-5, -1, 0, 1, 5]
for var i = 0; i < len(values); i = i + 1 {
var val int = values[i]
fmt.Printf("abs(%d) = %d\n", val, abs(val))
}
|
4. Bit Masking and Extraction
| Extract Specific Bits |
|---|
| package main
import fmt
// Extract bits 2-4 from a number
function extractBits(value int, start int, length int) int {
var mask int = (1 << length) - 1 // Create mask with 'length' ones
return (value >> start) & mask
}
var value int = 0b11010110 // 214 in decimal
var extracted int = extractBits(value, 2, 3) // Extract 3 bits starting at position 2
fmt.Printf("Extracted bits: %d\n", extracted) // Should be 5 (101)
|
Set Specific Bits
| Set/Clear/Toggle Bits |
|---|
| package main
import fmt
function setBit(value int, position int) int {
return value | (1 << position)
}
function clearBit(value int, position int) int {
return value & ~(1 << position)
}
function toggleBit(value int, position int) int {
return value ^ (1 << position)
}
var value int = 0b10100000 // 160
fmt.Printf("Original: %d\n", value)
fmt.Printf("Set bit 2: %d\n", setBit(value, 2)) // 164
fmt.Printf("Clear bit 5: %d\n", clearBit(value, 5)) // 128
fmt.Printf("Toggle bit 3: %d\n", toggleBit(value, 3)) // 168
|
5. Cryptographic Applications
Simple XOR Cipher
| Simple XOR Cipher |
|---|
| package main
import fmt
function xorEncrypt(data int, key int) int {
return data ^ key
}
function xorDecrypt(encrypted int, key int) int {
return encrypted ^ key // XOR is its own inverse
}
var message int = 42
var key int = 123
var encrypted int = xorEncrypt(message, key)
var decrypted int = xorDecrypt(encrypted, key)
fmt.Printf("Original: %d\n", message)
fmt.Printf("Encrypted: %d\n", encrypted)
fmt.Printf("Decrypted: %d\n", decrypted)
|
6. Data Structure Optimizations
Bit Set Implementation
| Bit Set Implementation |
|---|
| package main
import fmt
// Simple bit set for numbers 0-63
var bitSet int = 0
// Add number to set
function addToSet(set int, num int) int {
return set | (1 << num)
}
// Remove number from set
function removeFromSet(set int, num int) int {
return set & ~(1 << num)
}
// Check if number is in set
function isInSet(set int, num int) bool {
return (set & (1 << num)) != 0
}
// Example usage
bitSet = addToSet(bitSet, 5)
bitSet = addToSet(bitSet, 10)
bitSet = addToSet(bitSet, 15)
fmt.Printf("Is 5 in set: %t\n", isInSet(bitSet, 5)) // true
fmt.Printf("Is 7 in set: %t\n", isInSet(bitSet, 7)) // false
|
When to Use Bitwise Operations
Good Use Cases: - Flag management - Bit manipulation algorithms - Performance-critical code - Low-level system programming - Cryptographic operations - Data compression
Avoid When: - Code readability is more important than performance - Working with non-integer types - Complex logic that's clearer with regular operators
| Performance Comparison |
|---|
| // Fast: Bitwise operations
var isEven bool = (n & 1) == 0
var doubled int = n << 1
var halved int = n >> 1
// Slower: Regular operations
var isEven2 bool = (n % 2) == 0
var doubled2 int = n * 2
var halved2 int = n / 2
|
Memory Efficiency
Bitwise operations can significantly reduce memory usage:
| Memory Efficiency |
|---|
| // Instead of 8 boolean variables (8 bytes)
var flag1 bool = true
var flag2 bool = false
// ... 6 more flags
// Use a single integer (4 or 8 bytes)
var flags int = 0
flags |= 1 << 0 // Set flag1
flags &= ~(1 << 1) // Clear flag2
|
Common Pitfalls and Best Practices
1. Shift Amount Validation
| Shift Amount Validation |
|---|
| // Always validate shift amounts
function safeLeftShift(value int, amount int) int {
if amount < 0 or amount >= 64 {
return 0 // or handle error appropriately
}
return value << amount
}
|
2. Sign Extension with Right Shift
| Sign Extension with Right Shift |
|---|
| // Be aware of sign extension with negative numbers
var negative int = -8 // 11111000 (in 8-bit representation)
var shifted int = negative >> 1 // May be -4 (11111100) due to sign extension
|
3. Operator Precedence
| Operator Precedence |
|---|
| // Use parentheses for clarity
var result bool = (a & mask) != 0 // Clear intent
// vs
var result2 int = a & mask != 0 // Confusing due to precedence
|
4. Portability Considerations
| Portability Considerations |
|---|
| // Be careful with integer sizes across platforms
// Use specific types when bit layout matters
var flags uint32 = 0 // Explicit 32-bit integer
|
Advanced Techniques
Bit Reversal
| Bit Reversal |
|---|
| function reverseBits(n int) int {
var result int = 0
for var i = 0; i < 32; i = i + 1 {
result = (result << 1) | (n & 1)
n >>= 1
}
return result
}
|
Gray Code Conversion
| Gray Code Conversion |
|---|
| function binaryToGray(n int) int {
return n ^ (n >> 1)
}
function grayToBinary(n int) int {
var result int = n
for n > 0 {
n >>= 1
result ^= n
}
return result
}
|
Next Power of 2
| Next Power of Two |
|---|
| function nextPowerOfTwo(n int) int {
n--
n |= n >> 1
n |= n >> 2
n |= n >> 4
n |= n >> 8
n |= n >> 16
n = n + 1
return n
}
|
Conclusion
Bitwise operations in Harneet provide powerful tools for: - Efficient flag management - Performance optimization - Low-level programming - Algorithmic problem solving - Memory-efficient data structures
Master these operations to write more efficient and elegant code, especially in systems programming and performance-critical applications. Remember to balance performance gains with code readability and maintainability.