Skip to content

Instantly share code, notes, and snippets.

@apainintheneck
Last active May 15, 2022 05:18
Show Gist options
  • Save apainintheneck/016314d200fbe9a06fcadbf61a7f8f1d to your computer and use it in GitHub Desktop.
Save apainintheneck/016314d200fbe9a06fcadbf61a7f8f1d to your computer and use it in GitHub Desktop.
A bit manipulation refresher

Bit Manipulation Tips & Tricks

We all know that bit masking is occasionally necessary if you're dealing with data at the binary level but, if you're anything like me, you use it infrequently enough that you constantly have to lookup anything beyond the basics. This is just a spot to record all of the little tricks that I've come across in one place.

Basics

The three most common operations are to get, set and clear single bits. Those are usually pretty easy to remember but for thoroughness I'll note them down here.

# Get a bit
mask = (1<<bit)
num & mask

# Set a bit
mask = (1<<bit)
num | mask

# Clear a bit
mask = (1<<bit)
num ^ mask

The key operation is taking the number 1 and moving it left into position with the left shift operator <<.

# Set the nth bit
(1<<n)
# Ex.
(1<<0) # '1'
(1<<1) # '10'                          
(1<<2) # '100'                         
(1<<3) # '1000'                        
(1<<4) # '10000'
(1<<5) # '100000'
(1<<6) # '1000000'

Then, we use the basic bitwise operators &, | and ^ to change the original binary value. I've thrown in the other basic operators too.

  1010    1010    1010    1100    1100
& 1100  | 1100  ^ 1100    << 1    >> 1  ~ 1100
------  ------  ------  ------  ------  ------
  1000    1110    0110    1000    0110    0011

Tricks

These aren't really that useful but can be used to help you learn more about how bit manipulation works.

# Swap two numbers without creating another variable
a = a ^ b
b = a ^ b
a = a ^ b

# Check if a number is odd or even
(num & 1) == 0 # is even
(num & 1) == 1 # is odd

# Change case of char (for C/C++)
'A' | ' ' == 'a'
'a' & '_' == 'A'

Tips

Here is the "real skinny" as my dad has been saying recently for some reason. These are the things I tend to forget and that can be very useful when trying to manipulate those bits.

Count the number of set bits

# Count set bits (ones) in integer
# This is Kernigan's algorithm
# Loop only iterates once for each set bit in num
def pop_count(num)
  count = 0
  while(num > 0)
    num &= num - 1
    count += 1
  end
  count
end

First n bits

# Set the first n bits
(1<<n)-1
# Ex.
(1<<1)-1 # '1'
(1<<2)-1 # '11'
(1<<3)-1 # '111'
(1<<4)-1 # '1111'
(1<<5)-1 # '11111'
(1<<6)-1 # '111111'

# Clear first n bits
mask = (1<<n)-1
(num | mask) ^ mask
# Ex.
num = 250           # '11111010'
mask = (1<<4)-1     # '00001111'
(num | mask) ^ mask # '11110000'

# Replace first n bits
mask = (1<<n)-1
num = (num | mask) ^ mask # clear n bits from num
sub &= mask # clear upper bits of sub expression
num | sub # combine the two
# Ex.
num = 250                 # '11111010'
mask = (1<<4)-1           # '00001111'
num = (num | mask) ^ mask # '11110000'
sub = 85                  # '01010101'
sub &= mask               # '00000101'
num | sub                 # '11110101'

Ranges of bits

# Set a range of bits a-z inclusive
((1<<a)-1) ^ ((1<<(z+1))-1)
# Conceptually similar to creating two ranges and "subtracting" the smaller from the larger
# Ex.
((1<<0)-1) ^ ((1<<(3+1))-1) # '1111'
((1<<1)-1) ^ ((1<<(4+1))-1) # '11110'
((1<<2)-1) ^ ((1<<(5+1))-1) # '111100'
((1<<3)-1) ^ ((1<<(6+1))-1) # '1111000'
((1<<4)-1) ^ ((1<<(7+1))-1) # '11110000'
((1<<5)-1) ^ ((1<<(8+1))-1) # '111100000'

# Clear bits in a range a-z inclusive
mask = ((1<<a)-1) ^ ((1<<(z+1))-1)
(num | mask) ^ mask
# Ex.
num = 251                          # '11111011'
mask = ((1<<1)-1) ^ ((1<<(3+1))-1) # '00001110'
(num | mask) ^ mask                # '11110001'

# Replace bits in a range a-z inclusive
mask = ((1<<a)-1) & ((1<<z)-1) 
num = (num | mask) ^ mask # clear range of bits
sub = (sub<<a) & mask # clear bits not in range''
num | sub # combine the two  
# Ex.
num = 251                          # '11111011'
mask = ((1<<1)-1) ^ ((1<<(3+1))-1) # '00001110'
num = (num | mask) ^ mask          # '11110001'
sub = 18                           # '00010010'
sub = (sub<<1) & mask              # '00000100'
num | sub                          # '11110101'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment