Created
December 25, 2019 09:40
-
-
Save younisshah/780533c93bc25f7ff4b7eaaf01a1d0ea to your computer and use it in GitHub Desktop.
Intuitive explanation of bitmasks
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package bits | |
import "fmt" | |
/* | |
* Check this (https://alemil.com/bitmask) for full article | |
*/ | |
const ( | |
CanRun = 1 << 0 // 1 | |
CanBark = 1 << 1 // 2 | |
HasTail = 1 << 2 // 4 | |
CanJump = 1 << 3 // 8 | |
CanFly = 1 << 4 // 16 | |
IsCute = 1 << 5 // 32 | |
IsPet = 1 << 6 // 64 | |
) | |
// Bitmasks are a very useful way to compress multiple boolean flags in a single variable. | |
// It can reduce memory usage and operations on bits are basically as fast as they can get. | |
// In practice, any time you want to have multiple flags describing something in your application, | |
// a bitmask could be the right tool for the job. | |
func BitMask() { | |
// base animal | |
// 0 means that no properties has been assigned | |
// which translates to all boolean flags set to false | |
var noProperties uint32 = 0 | |
fmt.Println("noProperties", noProperties) | |
// bitwise OR | all the properties for a dog | |
var dogProperties uint32 = CanRun | CanBark | HasTail | CanJump | |
fmt.Println("dogProperties", dogProperties) // prints 15 | |
// bitwise OR | all the properties for a parrot | |
var parrotProperties uint32 = HasTail | CanFly | IsCute | |
fmt.Println("parrotProperties", parrotProperties) // prints 52 | |
// if we wanna toggle a property using bitwise ^. | |
// it'll either remove or add it depending on the current state of the bitmask | |
parrotProperties ^= IsCute // we don't think parrot is cute anymore, we'll turn it OFF | |
fmt.Println("not cute parrotProperties", parrotProperties) // prints 20 (52 - 32) | |
// no parrots are cute | |
parrotProperties ^= IsCute // turn it ON | |
fmt.Println("cute parrotProperties", parrotProperties) // prints 52 | |
// Turn off a flag no matter what the current state of the is | |
// bitwise AND & and bitwise NOT ^ | |
// ^IsCute means every flag except IsCute is set to true | |
parrotProperties &^= IsCute | |
fmt.Println("not parrotProperties", parrotProperties) // prints 52 | |
// How do we know which properties are set? | |
// Check if Dog has a tail | |
hasTail := (dogProperties & HasTail) == HasTail | |
// The above translates to: | |
// give me every mask that exists both to the left and to the right of the & ampersand character | |
// and compare == to see if what I need is the result. | |
fmt.Println("dog has a tail?", hasTail) | |
// remove dog's tail | |
dogProperties &^= HasTail | |
// Check again if Dog has a tail | |
hasTail = (dogProperties & HasTail) == HasTail | |
fmt.Println("dog has a tail?", hasTail) | |
// let's compare out dog and the parrot can see if anyone of them has a tail | |
anyoneHasTail := ((dogProperties | parrotProperties) & HasTail) == HasTail | |
fmt.Println("anyone has tail?", anyoneHasTail) | |
// or let's check if anyone is out pet | |
isMyPet := ((dogProperties | parrotProperties) & IsPet) == IsPet | |
fmt.Println("anyone is my pet?", isMyPet) | |
// also if we wanna know if EXACTLY on of the animals has a tail, but NOT both | |
exactlyOneTail := ((dogProperties ^ parrotProperties) & HasTail) == HasTail | |
fmt.Println("only one animal has tail?", exactlyOneTail) | |
// let's give back dog it's tail | |
dogProperties |= HasTail // toggle it's tail | |
fmt.Println("dog has a tail?", (dogProperties & HasTail) == HasTail) | |
// check if only one of them has a tail | |
exactlyOneTail = ((dogProperties ^ parrotProperties) & HasTail) == HasTail | |
fmt.Println("only one animal has tail?", exactlyOneTail) // false | |
// bit representation in memory | |
binary(noProperties) | |
// give it ability to run | |
noProperties |= CanRun | |
binary(noProperties) // 00000001: CanRun is second flag starting from the beginning (1 << 1) | |
// give it ability to bark | |
noProperties |= CanBark | |
binary(noProperties) // 00000011: CanRun is third flag starting from the beginning (1 << 2) | |
// make it cute | |
noProperties |= IsCute | |
binary(noProperties) // 00100011: IsCute is the 6th flag from the beginning. (1 << 5) | |
// bit shifting | |
binary(dogProperties)// 00001111 | |
// shift by 1 | |
// move all our flag forward. | |
// now the dog can't run, but it can fly | |
dogProperties <<= 1 | |
binary(dogProperties)// 00011110: | |
// check if dog can run | |
fmt.Println("can dog run?:", (dogProperties & CanRun) == CanRun) // false | |
fmt.Println("can dog fly?:", (dogProperties & CanFly) == CanFly) // true | |
dogProperties >>= 2 // shift right by 2. one shift for giving dog ability to run and one more to take dog's ability to jump | |
binary(dogProperties) | |
fmt.Println("can dog jump?:", (dogProperties & CanJump) == CanJump) | |
} | |
func binary(x uint32) { | |
fmt.Printf("%08b\n", x) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment