DEV Community

Cover image for Bitwise Operations
Ben Adams
Ben Adams

Posted on

Bitwise Operations

Perhaps looking through MDN, you might've come across the topics for Bitwise AND (&) and wondered what that meant. You've probably used the Logical AND (&&) operator before, so how is the single ampersand different from the double ampersand and what does bitwise mean?

Bitwise means that it operates on the actual bits (0s and 1s) of data that the computer understands. Numbers in JavaScript are stored as 64-bit floating point numbers, but when used with bitwise operators, numbers are converted to signed 32-bit Integers. Floating point numbers have a much greater number range (2.2250738585072014 * 10^-308 to 1.7976931348623158 * 10^308) and allow for decimals, but can be imprecise. For example, 0.1 + 0.2 = 0.30000000000000004 due to floating point rounding. Unsigned Integers are a bit simpler to understand because they are the binary equivalent of the decimal number. Unsigned Integers range from 0 to 2^N – 1, where N is how many bits. Depending on how signed integers are implemented (one's complement or two's complement), they range from -2^(N – 1) or -2^(N – 1) – 1 to 2^(N – 1) – 1, or -2,147,483,648 to 2,147,483,647 for a signed 32-bit integer.

JavaScript allows you to use non-base 10 numbers by using prefixes. You can write binary numbers by prefixing 0bor 0B before any amount of 0s and 1s. Octal, or base 8, numbers are prefixed with 0o or 0O and use 0 through 7. Hexadecimal numbers are prefixed with 0x or 0X, and use 0 through 9 as well as A through F. Hexadecimal numbers are typically used as shorthand representations of binary numbers since 32 consecutive 0s or 1s can be hard to read. Hexadecimal numbers are also used for representing colors in CSS.

Decimal Binary Octal Hexadecimal
0 0b0000 0o00 0x0
1 0b0001 0o01 0x1
2 0b0010 0o02 0x2
3 0b0011 0o03 0x3
4 0b0100 0o04 0x4
5 0b0101 0o05 0x5
6 0b0110 0o06 0x6
7 0b0111 0o07 0x7
8 0b1000 0o10 0x8
9 0b1001 0o11 0x9
10 0b1010 0o12 0xA
11 0b1011 0o13 0xB
12 0b1100 0o14 0xC
13 0b1101 0o15 0xD
14 0b1110 0o16 0xE
15 0b1111 0o17 0xF

Four binary numbers (or bits) can be represented with just one hexadecimal character, making it much easier on the eyes.

AND &

AND 0 1
0 0 0
1 0 1

Like logical AND (&&), bitwise AND returns 1 when both bits are 1, akin to returning true when both expressions are true. This could be used for coercing boolean values into the numbers 0 or 1.

0b01101001 & 0b11011101 === 0b01001001 // or 73
0x69 & 0xDD === 0x49 // or 73
true & true === 1
true & false === 0
Enter fullscreen mode Exit fullscreen mode

OR |

OR 0 1
0 0 1
1 1 1

Like logical OR (||), bitwise OR returns 1 when either or both bits match, instead of when either or both expressions evaluate true. It's an inclusive or.

0b01101001 | 0b11011101 === 0b11111101 // or 253
0x69 | 0xDD === 0xFD // or 253
Enter fullscreen mode Exit fullscreen mode

XOR ^

XOR 0 1
0 0 1
1 1 0

Like bitwise OR, XOR returns a 1 when either of the bits is 1, but not when both bits are 1.

0b01101001 ^ 0b11011101 === 0b10110100 // or 180
0x69 ^ 0xDD === 0xB4 // or 180
Enter fullscreen mode Exit fullscreen mode

NOT ~

Bitwise NOT returns the opposite for each bit.

~0b1101 === 0b11111111111111111111111111110010 // remember, we're dealing with 32-bit integers, so all of the preceding numbers were considered to be 0
~13 === -14
Enter fullscreen mode Exit fullscreen mode

Due to JavaScript's signed integers using Two's Complement to represent negative integers, ~A + 1 === -A, where A is any number. The leftmost bit is 0 for positive numbers and 1 for negative numbers.

Bit Shifting

Bit shifting is literally moving the original bits of the number to the left or right a specified number of times. There are three kinds of bit shifting. << is left bit shifting, where however many 0s are inserted at the right of the number. If the new number exceeds 32 bits, the overflow is discarded, so the new number could be the opposite sign of the original number. >> is sign-preserving right shift, where the left-most bit is repeated however many times. >>> is right shift that does not preserve the sign and 0s are inserted at the left. Left bit shifting is another way of multiplying that number by a power of two.

0b1101 << 3 === 0b1101000 // or 104
13 << 3 === 104 // or 13 * 2 ** 3
0b11001110111011011110001111110010 >> 4 === 0b11111100111011101101111000111111 // or -51454401
0b11001110111011011110001111110010 >>> 4 === 0b00001100111011101101111000111111 // or 216981055
Enter fullscreen mode Exit fullscreen mode

Practical Uses?

Bitwise operations can be faster than standard library calls, but it comes at cost of readability. These bitwise operators convert what would be 64-bit floating point numbers to 32-bit integers, so they can be used as a faster way of rounding a number to a whole number. By directly working with the 0s and 1s that contain the data we see, we are stripping away an abstraction layer for potential performance benefits.

Sources:

Top comments (0)