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 0b
or 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
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
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
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
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
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:
- https://standards.ieee.org/standard/754-2019.html
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_AND
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_NOT
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_OR
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Bitwise_XOR
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Unsigned_right_shift
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Right_shift
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Left_shift
Top comments (0)