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?
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.
0B before any amount of 0s and 1s. Octal, or base 8, numbers are prefixed with
0O and use 0 through 7. Hexadecimal numbers are prefixed with
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.
Four binary numbers (or bits) can be represented with just one hexadecimal character, making it much easier on the eyes.
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
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
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
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
~A + 1 === -A, where A is any number. The leftmost bit is 0 for positive numbers and 1 for negative numbers.
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
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.