A Real World Example of Bitwise AND, OR, and Left/Right Shift
Bitwise operators are a seldomly used part of the JavaScript language, being much more popular in low-level languages such as C. After my previous article on solving a popular coding challenge using the Bitwise XOR operator, we are now going to look at a real world example of using some other bitwise operators, by creating our own RGB to Hex and Hex to RGB colour convertors. These colour code convertors can also be created without using bitwise operators, but that wouldn't be as fun!
The Lineup
In this article, we will be using the following bitwise operators:
Please pay attention to the &
and |
symbols, as they look similar to their logical counterparts &&
and ||
.
In this article we can afford a bit more time to talk about how each of these operators functions.
Bitwise AND (&)
Bitwise AND is truthy when the two bits being compared are 1
, otherwise falsy.
x | y | x AND y |
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
Comparing two binary representations of numbers, each bit is compared against the other, and the resulting bit is set to 1
or 0
depending on the evaluation.
const result = 10 & 5 // 2
// compare each column:
1010 // 10
0110 // 5
----
0010 // 2
Bitwise OR (|)
Bitwise OR is truthy when when either or both bits are 1
.
x | y | x OR y |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
const result = 10 | 5 // 13
// compare each column
1010 // 10
0110 // 5
----
1110 // 13
Bitwise Left Shift
This operator shifts the bits to the left by the specified amount, while shifting in zero bits from the right. We will use an equal amount of bits for the comparisions, for easier visualisation.
// we can see that the singular 1 has been moved to the left by 4 places
const result = 8 << 4 // 128
0000 1000 << 4 = 1000 0000
// the bits have all been shifted to the left 3 places
const result = 55 << 3 = 440
0000 0011 0111 << 3 = 0001 1011 1000
Bitwise Right Shift
This operator shifts the bits to the right by the specified amount, where excess digits are pushed off the right.
const result = 256 >> 4 // 32
0001 0000 0000 = 0000 0010 0000
// here we see excess bits being pushed off the edge on the right
const result = 440 >> 6 // 6
0001 1011 1000 = 0000 0000 0110
RGB to Hex
Let's take the Hex colour code #157f4c
. First we must understand what the Hex colour code represents. Rather than being a large hexdecimal number (0x157f4c = 1408844)
, we must split this into groups of 2, each representing the value of R, G, and B.
Hexadecimal | Decimal | Binary |
---|---|---|
0x15 | 21 | 00010101 |
0x7f | 127 | 01111111 |
0x4c | 76 | 01001100 |
Our RGB value is 21, 127, 76
Our goal is to make one long 24-bit binary representation of our RGB values, as such:
21 | 127 | 76 |
---|---|---|
00010101 | 01111111 | 01001100 |
00010101 01111111 01001100
To achieve this, we need to move the bits for the R and G values to the left, by 16 and 8 bits respectively. For this we will use the Bitwise Left Shift (<<)
operator.
// Binary representations are in 24-bit space
// R value
00000000 00000000 00010101 << 16 = 00010101 00000000 00000000
// G value
00000000 00000000 01111111 << 8 = 00000000 01111111 00000000
// B value
00000000 00000000 01001100
We have three binary numbers, and we need to create one long 24-bit binary number with them. We can achieve this using the Bitwise OR (|)
operator, as follows:
// R | G, compare each column using the rules for the bitwise OR operator
00010101 00000000 00000000 |
00000000 01111111 00000000
--------------------------
00010101 01111111 00000000
// and now this new value | B
00010101 01111111 00000000 |
00000000 00000000 01001100
--------------------------
00010101 01111111 01001100 // the desired binary representation of our RGB values
Pay attention to how the bitwise OR is working in the above example, concatenating the binary values into one long value.
Let us now write a function to tie this all together.
function rgbToHex(rgb) {
// shifting the bits to the left and comparing with bitwise OR
const binaryRGB = rgb[0] << 16 | rgb[1] << 8 | rgb[2]
// convert the binaryRGB string to a hexadecimal (base-16) string
const hex = binaryRGB.toString(16)
// add '#' to the beginning. Make sure the returned value is always 6 characters long, padding with a 0 if not.
return `#${hex.padStart(6, '0')}`
}
console.log(rgbToHex([21, 127, 76])) // values taken from a form etc.
// '#157f4c'
Hex to RGB
And now to do the inverse, taking a Hex colour value and returning RGB values. Let's use the same Hex code that the previous section returned, to prove that it is correct by returning our original RGB values.
First of all, we need to work with a real hexadecimal value:
const hex = '#15014c'.replace('#', '0x') // '0x15014c'
If we view this hexadecimal in its binary representation again, we get:
00010101 01111111 01001100
R | G | B |
---|---|---|
00010101 | 01111111 | 01001100 |
21 | 127 | 76 |
Now we need to store each of the R, G, and B values in their own variables. In order to create the correct binary number for each of these values, we must now bitwise right shift the R and G values by 16 and 8 bits respectively.
00010101 01111111 01001100 >> 16 = 00000000 00000000 00010101 // R (21)
00010101 01111111 01001100 >> 8 = 00000000 00010101 01111111 // G (4319)
00010101 01111111 01001100 // G (219948)
Wait, these values are incorrect! We need to take one more very important step, which would leave us with just the required bits.
Comparing the binary representation with Bitwise AND (&)
against 11111111
will leave us with just the last 8 bits of information.
// Compare the columns
// R
00000000 00000000 00010101 &
00000000 00000000 11111111
--------------------------
00000000 00000000 00010101 // (21)
// G
00000000 00010101 01111111 &
00000000 00000000 11111111
--------------------------
00000000 00000000 01111111 // (127)
// B
00010101 01111111 01001100 &
00000000 00000000 11111111
--------------------------
00000000 00000000 01001100 // (76)
Pay attention to how the bitwise AND operator works in the above examples, and how it leaves us with only the required information.
Let us now tie this all together in a function.
function hexToRgb(hexColour) {
// replace '#' with '0x' to give us a real hexadecimal string to work with
const hex = hexColour.replace('#', '0x')
// right shift the respective bits
// followed bit masking with bitwise AND against '11111111' (0xff in hexadecimal)
const r = hex >> 16 & 0xff
const g = hex >> 8 & 0xff
const b = hex & 0xff
return [r, g, b] // return an array of the RGB values
}
console.log(hexToRgb('#15014c'))
// [ 21, 1, 76 ]
Conclusion
In this article we saw a real world example of using bitwise operators in JavaScript, by building a RGB to Hex and Hex to RGB colour convertors. If you have never dealt with bitwise operators before, there is a lot to take in here. However there is no better way to learn a concept than by actually building something!
If you are looking for further reading on the topic, try the following articles:
Top comments (0)