© Russ Ferguson and Keith Cirkel 2017
Russ Ferguson and Keith CirkelJavaScript Recipes10.1007/978-1-4302-6107-0_5

5. Working with Bitwise Operations Against 32-Bit Integers

Russ Ferguson and Keith Cirkel2
(1)
Ocean, New Jersey, USA
(2)
London, UK
 

What Are 32-Bit Integers?

Problem

You want to be able to determine the difference between a 32-bit integer and another numeric value (such as the default number—64-bit floating point).

Solution

A 32-bit integer is vastly different from a variable bit floating-point number, which is the standard type of number in JavaScript. 32-bit integers can only be created using bitwise operators , which will be covered all throughout this chapter.

The Code

3.141; // Standard variable bit Number literal
0b; // 4, the same as ⌊2 * 2⌋
2 << 4; // 32, the same as ⌊2 * 24⌋ or ⌊2 * 16⌋
16 << 4; // 256, the same as ⌊16 * 24⌋ or ⌊16 * 16⌋
var myNum = 1234, pow = 16;
myNum << pow; // 80.871.424, the same as ⌊1234 * 216⌋ or ⌊1234 * 65536⌋
Listing 5-1.
Examples of 32-Bit Integers

How It Works

The JavaScript interpreter, when encountering any bitwise operator , will coerce the given floating-point number into a 32-bit integer. It removes any decimal places from the number, because 32-bit integers can only be integer (whole) values. 32-bit integers are also limited in their range of values, either a signed range or an unsigned range, depending on the bitwise operator that’s used.
Unsigned integers can only have positive values, and so they use the full 32-bits to designate the value of the positive integer. They have a minimum value of 0 and a maximum value of +4,294,967,295. How does this work? Well each binary bit has two values, 1 or 0, and combining 32 of them gives you 232 possible values—precisely 4,294,967,296 different values—but of course the value 0 needs to be stored somehow, so that leaves 4,294,967,295 remaining values.
Signed integers have a minimum value of -2,147,483,647 and a maximum value of +2,147,483,647, this is because the first of the 32 bits is reserved to designate the “sign” (i.e., whether the integer is positive or negative). That leaves 31 bits remaining to designate the value of the integer: 231 is, of course 2,147,483,648, which is enough to store 2,147,483,647 Integers, plus a 0 value. The 32nd bit is often called the “sign bit” or sometimes the MSB (Most Significant Bit) . If the number is negative, the MSB is 1; if the number is positive, it’s 0.
Signed integers have a further complexity: they use a system called “Two’s Complement ”. In Two’s Complement, the integer’s negative values have their bits reversed and begin counting down from -1. To give an example of this, the number +9 in binary notation is 0b00000000000000000000000000001001 and the MSB or “sign bit” (left most bit, after “0b”) is 0, indicating this number is a positive number. You may expect the Integer -9 to be 0b10000000000000000000000000001001, where all that has changed is the MSB is set to 1 rather than 0; however, negative integers count up from -2,147,483,647, meaning that 0b10000000000000000000000000001001 is equal to 2,147,483,657. The actual 32-bit signed integer for -9 then is 0b11111111111111111111111111110111.

Multiplying by Powers of 2 with the Left Shift (<<) Operator

Problem

You want to take a signed 32-bit integer (that is, a whole number with a maximum value of 2,147,483,647 and a minimum value of -2,147,483,647) and multiply it by a power of two.

Solution

Bitwise shift operators are useful for doing low-level binary computation, especially on numbers (although they convert them to 32-bit signed integers). The left shift bitwise operator can be used to shift binary bits to the left, which allows you to multiply a 32-bit integer by a power of 2.

The Code

2 << 0; // 2, the same as ⌊2 * 20⌋ or ⌊2⌋. This returns a coerced 32-bit Signed Integer
2 << 1; // 4, the same as ⌊2 * 2⌋
2 << 4; // 32, the same as ⌊2 * 24⌋ or ⌊2 * 16⌋
16 << 4; // 256, the same as ⌊16 * 24⌋ or ⌊16 * 16⌋
var myNum = 1234, pow = 16;
myNum << pow; // 80.871.424, the same as ⌊1234 * 216⌋ or ⌊1234 * 65536⌋
Listing 5-2.
Multiplying by Powers of 2 with the Left Shift (<<) Operator

How It Works

The JavaScript interpreter, when the left shift bitwise operator is used, converts the number into a signed 32-bit binary integer (read more about signed 32-bit integers in the first section of this chapter).
The bitwise left shift operator simply takes the binary value of the left hand operand and moves all bits left by the specified right hand operand. Any spare bits are filled with 0 values. Take a look at Figure 5-1 to see how the number +9 can be shifted right by 1 bit, to become +18. You can see how all of the bits have moved one position to the left, and any extra spaces on the right have been filled with 0 values.
A315054_1_En_5_Fig1_HTML.jpg
Figure 5-1.
Illustration of a bitwise left shift of 1 against +9
There are some fairly major caveats with using bitwise shift operators that can lead to undesirable results. First, as hinted at by their mathematical equivalents, all numbers are floored (meaning the decimal values are removed). Secondly, continuing to use the left shift operator to shift bits left will eventually turn positive numbers into negative ones, as bits with a value of 1 can be shifted up. As explained, because negative numbers have reversed bits, the shifting left until you flip the sign will also produce a completely different number; for example, the operation +1073741825 << 1 returns -2,147,483,646, but +1073741825 * 21 is actually equal to 2,147,483,650 when not faced with the limitations of signed 32-bit integers. Figure 5-2 illustrates this concept.
A315054_1_En_5_Fig2_HTML.jpg
Figure 5-2.
Illustration of a bitwise left shift of 1 against +1,073,741,825
Also, 32-bit integers have a maximum value of 2,147,483,647, so attempting to left shift numbers of a greater value leads to integer-overflows. For example, 2147483647 << 1 will return -2. See Figure 5-3 for this illustration.
A315054_1_En_5_Fig3_HTML.jpg
Figure 5-3.
Illustration of a bitwise left shift of 1 against +2,147,483,647
The whole notion of bit shifting can become quite complex because of all of the caveats, and while you are generally safe with small numbers, large numbers can easily trip you up as the sign bit changes around, changing the behavior of the number. If in doubt, it is probably best to leave bitwise shift operations alone and instead use something like Math.pow(), discussed later in this book. As an illustration of how you can trip up using bitwise shift operators, Table 5-1 shows how the number +9, having its bits shifted left, can cause undesirable effects. You may see the left shift bitwise operator in real-world code, where people attempt to coerce numbers into signed 32-bit integers, using a << 0. This doesn’t actually shifting any bits, but instead is simply coercing a number to 32-bit. Other real-world uses of this deal with binary data, such as image or audio data, or Node.JS binary buffers.
Table 5-1.
Bitwise Left Shift Example Scale
Expression
Math Equivalent
Binary
Value
9 << 0
9 * 2 0
0b00000000000000000000000000001001
9
9 << 1
9 * 2 1
0b00000000000000000000000000010010
18
9 << 2
9 * 2 2
0b00000000000000000000000000100100
36
9 << 3
9 * 2 3
0b00000000000000000000000001001000
72
9 << 4
9 * 2 4
0b00000000000000000000000010010000
144
9 << 5
9 * 2 5
0b00000000000000000000000100100000
288
9 << 6
9 * 2 6
0b00000000000000000000001001000000
576
...
...
...
...
9 << 27
9 * 2 27
0b01001000000000000000000000000000
1,207,959,552
9 << 28
9 * 2 28
0b10010000000000000000000000000000
-1,879,048,192
9 << 29
9 * 2 29
0b00100000000000000000000000000000
536,870,912
9 << 30
9 * 2 30
0b01000000000000000000000000000000
1,073,741,824
9 << 31
9 * 2 31
0b10000000000000000000000000000000
-2,147,483,648
9 << 32
9 * 2 32
0b00000000000000000000000000001001
9

Dividing by Powers of 2 with the Sign-Propagating Right Shift (>>) Operator

Problem

You want to take a signed 32bit integer (that is, a whole number with a maximum value of 2,147,483,647 and a minimum value of -2,147,483,647) and divide it by a power of two.

Solution

Bitwise shift operators are useful for doing low-level binary computation, especially on numbers (although they convert them to 32-bit signed integers). The right shift bitwise operator can be used to shift binary bits to the right, while still propagating the sign bit (in other words, it remains positive or negative), which allows you to divide a 32-bit integer by a power of 2.

The Code

2 >> 0; // 2, the same as ⌊2 ÷ 20⌋or ⌊2⌋. This returns a coerced 32-bit Signed Integer
2 >> 1; // 1, the same as ⌊2 ÷ 21⌋ or ⌊2 ÷ 2⌋
2 >> 4; // 0, the same as ⌊2 ÷ 24⌋ or ⌊2 ÷ 16⌋
16 >> 4; // 1, the same as ⌊16 ÷ 24⌋ or ⌊16 ÷ 16⌋
var myNum = 1234, pow = 16;
myNum >> pow; // 0, the same as ⌊1234 ÷ 216⌋ or ⌊1234 ÷ 65536⌋
Listing 5-3.
Dividing by Powers of 2 with the Sign-PROPAGATING Right Shift (>>) Operator

How It Works

The sign-propagating right shift bitwise operator works almost the same as the left shift bitwise operator. When a sign-propagating right shift bitwise operator is used, the JavaScript interpreter converts the number into a signed 32-bit binary integer (read more about signed 32-bit integers in the first section of this chapter).
The bitwise sign-propagating right shift operator simply takes the binary value of the left hand operand and moves all bits—except for the MSB (Most Significant Bit)—right by the specified right hand operand. Any spare bits are filled with the same value as the MSB. Take a look at Figure 5-4 to see how the number +9 can be shifted right by 1 bit, to become +4. You can see how all of the bits have moved one position to the left, and any extra spaces on the right have been filled with 0 values, which is the value of the sign bit (MSB). Also notice that the number -9 shifted by 1 bit takes on the value of -5, while all new inserted bits are set to 1 (the value of the sign bit, or MSB).
A315054_1_En_5_Fig4_HTML.jpg
Figure 5-4.
Illustration of a bitwise sign-propagating right shift of 1 against +9 and -9
There are some fairly major caveats with using bitwise shift operators that can lead to undesirable results.
First, as hinted at by their mathematical equivalents, all numbers are floored (meaning the decimal values are removed). Also, signed 32-bit integers have a maximum value of 2,147,483,647, so attempting to right shift numbers of a greater value leads to integer-overflows. For example 2147483648 >> 1 will return-1,073,741,824. See Figure 5-5 for this illustration.
A315054_1_En_5_Fig5_HTML.jpg
Figure 5-5.
Illustration of a sign-propagating bitwise right shift of 1 against +2,147,483,648
The whole notion of bit shifting can become quite complex because of all of the caveats, and while you are generally safe with small numbers, large numbers can easily trip you up as the sign bit changes around, changing the behavior of the number. If in doubt, it is probably best to leave bitwise shift operations alone and instead use something like Math.pow(), discussed later in this book. As an illustration of how you can trip up using bitwise shift operators, Table 5-2 shows how the number +258, having its bits shifted right, can cause undesirable effects. You may see the sign-propagating right shift bitwise operator in real-world code, where people attempt to coerce numbers into signed 32-bit Integers using a >> 0. This doesn’t actually shift any bits, but instead simply coerces a number to 32-bit. Other real-world uses of this deal with binary data, such as image or audio data, or Node.JS binary buffers.
Table 5-2.
Bitwise Sign-Propagating Right Shift Example Scale
Expression
Math Equivalent
Binary
Value
258 >> 0
258 * 2 0
0b00000000000000000000000100000010
258
258 >> 1
258 * 2 1
0b00000000000000000000000010000001
129
258 >> 2
258 * 2 2
0b00000000000000000000000001000000
64
258 >> 3
258 * 2 3
0b00000000000000000000000000100000
32
258 >> 4
258 * 2 4
0b00000000000000000000000000010000
16
258 >> 5
258 * 2 5
0b00000000000000000000000000001000
8
258 >> 6
258 * 2 6
0b00000000000000000000000000000100
4
258 >> 7
258 * 2 6
0b00000000000000000000000000000010
2
258 >> 8
258 * 2 7
0b00000000000000000000000000000001
1
258 >> 9
258 * 2 8
0b00000000000000000000000000000000
0

Using the Zero-Fill Right Shift (>>>) Operator to Ensure a Variable Is a 32-Bit Unsigned Integer

Problem

You want to take a 32-bit unsigned integer (that is, a whole number with a maximum value of 4,294,967,295 and a minimum value of 0) and divide it by a power of two, or you want to take an existing number and coerce it into a 32-bit unsigned integer.

Solution

The zero-fill right shift operator is similar to the sign-propagating right shift operator in that it coerces a number into a 32-bit integer, and it will shift bits right based on the value of the right hand operand. There is a subtle but huge difference though—while the sign-propagating right shift operator coerces its return value into a 32-bit signed integer, the zero-fill right shift operator coerces its return value into an unsigned integer.

The Code

2 >>> 0; // 2, the same as ⌊2 ÷ 20⌋or ⌊2⌋. This returns a coerced 32-bit Unsigned Integer
2 >>> 1; // 1, the same as ⌊2 ÷ 21⌋ or ⌊2 ÷ 2⌋
2 >>> 4; // 0, the same as ⌊2 ÷ 24⌋ or ⌊2 ÷ 16⌋
16 >>> 4; // 1, the same as ⌊16 ÷ 24⌋ or ⌊16 ÷ 16⌋
var myNum = 1234, pow = 16;
myNum >>> pow; // 0, the same as ⌊1234 ÷ 216⌋ or ⌊1234 ÷ 65536⌋
Listing 5-4.
Use the Zero-Fill Right Shift (>>>) Operator to Ensure a Variable Is a 32-Bit Unsigned Integer

How It Works

The zero-fill right shift bitwise operator works almost the same as the sign-propagating right shift bitwise operator, but because it uses UNSIGNED integers the resulting conversions can be drastically different. The zero-fill right shift operator will not copy bits from the MSB to any new bits (i.e., it doesn’t propagate the sign), instead filling them with zeros. The right hand operand will be converted into an unsigned 32-bit binary integer, which is to say a number ranging from 0 to 4,294,967,295 (read more about signed 32-bit integers in the first section of this chapter).
There are no reserved bits on an unsigned integer; every bit is used up to calculate the number, unlike a signed integer which reserves the MSB for the sign.
The bitwise zero-fill right shift operator simply takes the binary value of the left hand operand and moves all bits right by the specified right hand operand; any spare bits are filled with zeros. Take a look at Figure 5-6 to see how the number +9 can be shifted right by 1 bit to become +4.
A315054_1_En_5_Fig6_HTML.jpg
Figure 5-6.
Illustration of a bitwise zero-fill right shift of 1 against +9
There are some fairly major caveats with using bitwise shift operators, that can lead to undesirable results. First, as hinted at by their mathematical equivalents, all numbers are floored (meaning the decimal values are removed). Also, unsigned 32-bit integers have a maximum value of 4,294,967,295 and cannot cope with negative numbers. Attempting to right shift negative numbers will lead to unexpected results, due to the way negative signed 32-bit numbers have their bits reversed, so -9 >>> 1 will return 2,147,483,643. See Figure 5-7 for this illustration.
A315054_1_En_5_Fig7_HTML.jpg
Figure 5-7.
Illustration of a zero-fill bitwise right shift of 1 against -9
The whole notion of bit shifting can become quite complex because of all of the caveats. You may see the zero-fill right shift bitwise operator in real-world code, where people attempt to coerce numbers into unsigned 32-bit integers, using a >>> 0. This does not actually shift any bits, but instead simply coerces a number to 32-bit . This can be useful when dealing with the limitations of other systems or APIs, or binary buffers in Node.JS, but should be used with great caution because of the caveats listed.

Using the Bitwise NOT (∼) Operator to Swap All Binary Bits in a 32-Bit Integer

Problem

You have a number that you’d like to coerce into a 32-bit integer, and swap every positive bit into a negative one, and every negative one into a positive. You might want to do this to toggle a flag value or get the Two’s Complement binary opposite of a number.

Solution

The bitwise NOT (∼) operator can be used to swap all of the binary bits in a number. Because of the Two’s Complement, it does not simply just flip the sign of a number, but instead does the equivalent of -(n + 1).

The Code

∼0; // -1, the same as ⌊ -(0 + 1) ⌋
∼1; // -2, the same as ⌊ -(1 + 1) ⌋
∼2; // -3, the same as ⌊ -(2 + 1) ⌋
∼3; // -4, the same as ⌊ -(3 + 1) ⌋
∼4; // -5, the same as ⌊ -(4 + 1) ⌋
∼5; // -6, the same as ⌊ -(5 + 1) ⌋
∼6; // -7, the same as ⌊ -(6 + 1) ⌋
∼7; // -8, the same as ⌊ -(7 + 1) ⌋
∼8; // -9, the same as ⌊ -(8 + 1) ⌋
∼2147483648; // 2147483647, reaches the integer overflow limit
∼-1; // 0, the same as ⌊ -(-1 + 1) ⌋
∼-27; // 26, the same as ⌊ -(-27 + 1) ⌋
∼-2423; // 2422, the same as ⌊ -(-2423 + 1) ⌋
∼-2147483648;  // 2147483647, the same as ⌊ -(-2147483648 + 1) ⌋
Listing 5-5.
Use the Bitwise NOT (∼) Operator to Round Floating-Point Numbers

How It Works

The bitwise NOT operator first coerces the operand into a signed 32-bit binary integer. The bitwise NOT operator simply takes each bit and flips it to be the opposing value, so a 1 becomes a 0 and a 0 becomes a 1. Take a look at Figure 5-8 to see how the number +9 can be shifted right by 1 bit to become +4.
A315054_1_En_5_Fig8_HTML.jpg
Figure 5-8.
Illustration of a bitwise NOT operation against +12345
There are some fairly major caveats with using the bitwise NOT operators, that can lead to undesirable results. Just like the shift operators, as hinted at by their mathematical equivalents , all numbers are floored (meaning the decimal values are removed). Also, signed 32-bit integers have a maximum value of 2,147,483,647, so attempting to use numbers of a greater value leads to integer overflows. For example ∼2147483648 will return 2147483647, where you would expect this to be -2147483649. See Figure 5-9 for this illustration.
A315054_1_En_5_Fig9_HTML.jpg
Figure 5-9.
Illustration of a bitwise NOT operation against +2147483648
In reality, the bitwise NOT operator has a limited application, but like most of JavaScript, it does have some real-world misuses.
You may see real-world uses of the NOT operator in indexOf() operations , for example ∼array.indexOf(4). This is an abuse of the bitwise NOT operator, as indexOf() will return -1 for a negative match, and values of 0 and above for a positive match. -1 and +1 and above coerce to the Boolean true, while 0 coerces to the Boolean false. However, using the bitwise NOT operator turns a 0 into -1 (true) and -1 to 0 (false), any other positive numbers still remain true (any number that isn’t ±0 will coerce to true). This complex shifting means that a negative match will end up coercing to false, while a positive match will end up coercing to true. Unfortunately, this reads quite badly and the logic is very complex to explain to beginners and in the (somewhat unlikely) event you have an array or string with a greater than 32-bit length, you’ll hit integer overflow problems as described previously! It is always preferable to use the more explicit array.indexOf(4) !== -1.
Another popular but misguided use case is the so-called “double NOT” operator , which is a concatenation of two bitwise NOT operators against a number, for example ∼∼3.141. In the case of ∼∼3.141, the first bitwise NOT will flip the number as usual, but also floor it, so it becomes -4; the second bitwise NOT will flip it again so it becomes +3. This gives the appearance of the number being floored, similar to Math.floor() and is supposedly “more performant” than its Math.floor() cousin. However, this pattern comes with all the baggage that the bitwise NOT operator comes with; the number is coerced to a 32-bit signed integer and the “flooring” mechanism does not operate the same way as Math.floor(). The flooring mechanism of coercing a number to a 32-bit Integer simply removes the decimal place, so ∼∼3.141 becomes 3, while ∼∼-3.141 becomes -3. The real Math.floor() function , however, behaves differently, in that 3.141 becomes 3, but -3.141 becomes -4.
Because of the caveats around this bitwise operator , avoid using it outside of its intended context—dealing with binary data, such as for turning off flags.

Using Bitwise AND (&) Operator to Get a Value of All Equal Bits in a 32-Bit Integer

Problem

You have a number that you’d like to coerce into a 32-bit integer and compare against another coerced 32-bit integer, where every positive bit in both integers creates a new integer with matching positive bits. This can be very useful for checking for a flag’s existence in bit fields or bit masks.

Solution

The bitwise AND operator (&) compares 32-bit integers for their bit values and returns a new 32-bit integer that’s a summation of all of the 1 or “On” bits in both of the numbers in the expression.

The Code

14 & 9; // 8
92 & 46; // 12
92 & 0; // 0
0 & 0; // 0
96 & -1; // 96
Listing 5-6.
Check If a Number Is Even Using Bitwise AND (&)

How It Works

The bitwise AND operator first coerces the left and right hand operands into signed 32-bit binary integers (read more about signed 32-bit integers in the first section of this chapter). It then looks at each bit between the left and right hand operands and sets each bit in the resulting integer based on the results. The resulting bit in each position is 1 if the left hand and right hand bits in that position equal 1; otherwise, the resulting bit in that position is 0. Take a look at Figure 5-10 for a visual representation of this.
A315054_1_En_5_Fig10_HTML.jpg
Figure 5-10.
Illustration of a bitwise AND operation of +92 against +46
Because the resulting integer is a combination of all 1 or “on” bits in both operands, there are certain sets of integers that will have set values; for example, using 0 as either operand will result in the number being 0, as illustrated in Figure 5-11. Similarly if one of the operands is -1, then the resulting integer will be the other operand, as illustrated in Figure 5-12.
A315054_1_En_5_Fig11_HTML.jpg
Figure 5-11.
Illustration of a bitwise AND operation of +92 against 0
A315054_1_En_5_Fig12_HTML.jpg
Figure 5-12.
Illustration of a bitwise AND operation of +92 against -1
Just like most bitwise operators, there is a small set of use cases for the bitwise AND operator, and it can frequently be misused, for example to check if a number is even, you might consider using (x & 1) === 0 of course that would come with all of the caveats of bitwise operators, including the conversion to 32-bit, meaning you could not use this for large numbers. It works best when checking values in bit fields or bit masks.

Using Bitwise OR (|) Operator to Get a Value Positive Bits on One or Both Operands

Problem

You have a number that you’d like to coerce into a 32-bit integer and compare against another coerced 32-bit integer, where every positive bit in either operand creates a new integer with matching positive bits. This can be very useful for checking assigning a flag to a bit field or bit mask.

Solution

The bitwise OR operator (|) does just this—it compares 32-bit Integers for their bit values and returns a new 32-bit integer which is a summation of all of the 1 or “On” bits in either of the numbers’ operands.

The Code

14 | 9; // 15
92 | 46; // 126
92 | 0; // 92
0 | 0; // 0
96 | -1; // -1
Listing 5-7.
Check If a Number Is Even Using Bitwise AND (&)

How It Works

The bitwise OR operator first coerces the left and right hand operands into signed 32-bit binary integers (read more about signed 32-bit Integers in in the first section of this chapter). It then looks at each bit between the left and right hand operands and sets each bit in the resulting integer based on the results. The resulting bit in each position is 1 if the left hand or right hand operand’s bits in that position equal 1; otherwise, the resulting bit in that position is 0. Take a look at Figure 5-13 for a visual representation of this.
A315054_1_En_5_Fig13_HTML.jpg
Figure 5-13.
Illustration of a bitwise OR operation of +92 against +46
Because the resulting integer is a combination of all 1 or “on” bits in either operand, there are certain sets of integers that will have set values; for example, using 0 as either operand will result in the number being equal to the other operand, as illustrated in Figure 5-14. Similarly, if one of the operands is -1, then the resulting integer will be -1, as illustrated in Figure 5-15.
A315054_1_En_5_Fig14_HTML.jpg
Figure 5-14.
Illustration of a bitwise OR operation of +92 against 0
A315054_1_En_5_Fig15_HTML.jpg
Figure 5-15.
Illustration of a bitwise OR operation of +92 against -1
just like most bitwise operators, there is a small set of use cases for the bitwise OR operator, and it can frequently be misused. You may find code that rounds numbers using | 0, for example 293.3 | 0, which of course comes with all the caveats that bitwise operators provide. For example, this will not work with large numbers and does not use the same flooring mechanism as Math.floor(). For example using -3.141 | 0 results in 3, while Math.floor(-3.141) results in -4. It works best when combining values in bit fields or bit masks.

Using the Bitwise XOR (^) Operator to Get a Value of Differing Bits in Each Operand

Problem

You have a number that you’d like to coerce into a 32-bit integer and compare against another coerced 32-bit integer, where every differing bit in either operand creates a new integer with matching positive bits. This can be very useful for checking assigning a flag to a bit field or bit mask.

Solution

The bitwise XOR operator (^) compares 32-bit integers for their bit values and returns a new 32-bit integer, which is a summation of all of the differing bits (where one operand has a 0 and the other a 1) in the expression.

The Code

14 ^ 9; // 7
92 ^ 46; // 114
92 ^ 0; // 92
0 ^ 0; // 0
96 ^ -1; // -97
Listing 5-8.
Check If a Number Is Even Using Bitwise AND (&)

How It Works

The bitwise XOR operator first coerces the left and right hand operands into signed 32-bit binary integers (read more about Signed 32-bit Integers in the first section of this chapter). It then looks at each bit between the left and right hand operands and sets each bit in the resulting integer based on the results. The resulting bit in each position is 1 if the left hand or right hand bits in that position equal 1. If they are both 1 or both 0, then the resulting bit will be 0. Take a look at Figure 5-16 for a visual representation of this.
A315054_1_En_5_Fig16_HTML.jpg
Figure 5-16.
Illustration of a bitwise XOR operation of +92 against +46
Because the resulting integer is a combination of all differing bits in both operands, there are certain sets of integers that will have set values; for example, using 0 as either operand will result in the number equaling the other operand, just like in a bitwise OR, as illustrated in Figure 5-17. Interestingly if one of the operands is -1 then the operation is the equivalent act of using the bitwise NOT (∼) on the other operand; see Figure 5-18.
A315054_1_En_5_Fig17_HTML.jpg
Figure 5-17.
Illustration of a bitwise XOR operation of +92 against 0
A315054_1_En_5_Fig18_HTML.jpg
Figure 5-18.
Illustration of a bitwise XOR operation of +92 against -1
Just like most bitwise operators, there is a small set of use cases for the bitwise XOR operator, and it can frequently be misused. It works best when checking values in bit fields or bit masks.

Converting RGB Color to Hexadecimal and Back Using the Bitwise Signed Shift (>>, <<) Operators and the Bitwise AND (&) Operator

Problem

You want to be able to convert hexadecimal colors into an RGB notation or take an RGB notation and convert that into a hexadecimal color.

Solution

This is a great application of bitwise operators , as the numbers required (0 - 255) fit inside a 32-bit integer, and shifting can be used quite elegantly to extract the right number components.

The Code

function hex2rgb(hex) {
    return {
        r: hex >> 16,
        g: hex >> 8 & 255,
        b: hex & 255,
    };
}
function rgb2hex(r, g, b) {
    return ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
}
console.log(hex2rgb(0xFFFFFF));
console.log(hex2rgb(0xFF69B4));
console.log(hex2rgb(0xDAA520));
console.log(rgb2hex(255, 255, 255));
console.log(rgb2hex(255, 105, 180));
console.log(rgb2hex(218, 165, 32));
Listing 5-9.
Convert RGB Color to Hexadecimal Using the Bitwise AND (&) Operator
The output is:
{r: 255, g: 255, b: 255}
{r: 255, g: 105, b: 180}
{r: 218, g: 165, b: 32}
Ffffff
ff69b4
daa520

How It Works

We have two functions , hex2rgb and rgb2hex. Each one takes the respective value (hex2rgb takes a hex and rgb2hex takes red, green, and blue values). Each one uses bit shifting to take their value and shifts them to the right binary positions. This can seem pretty daunting at first, but let’s see what happens, line by line.

Hex2RGB

function hex2rgb(hex) {
This is a function declaration (you can read more about functions in Chapter 12). The function requires one argument named hex. You pass it hexadecimal numbers. In this example, we’ll pass it the hexadecimal 0xFF69B4 (the CSS color “hot pink” which has an RGB value of 255, 105, 180). If the hex were to be coerced into a number, it would be 16738740. This number is the R, G, and B values combined.
r: hex >> 16,
Here, we take our hex value and shift the number 16 bits to the right—assuming our hex was 0xFF69B4, and therefore our parsed hex (16738740), shifting right 16 bits, or ⌊16738740 ÷ 216⌋ returns 255. This removes the “green” and “blue” portions of this integer; see Figure 5-19 for an illustration of this.
A315054_1_En_5_Fig19_HTML.jpg
Figure 5-19.
Illustration of a bitwise sign-propagating right shift of 16 against 16738740
g: hex >> 8 & 255,
This line takes the hex value and shift the number 8 bits to the right—assuming our hex was 0xFF69B4 (16738740), shifting right 16 bits, or ⌊16738740 ÷ 28⌋ gives us 65385. This removes the “blue” section from the hex; see Figure 5-20 for an illustration of this. The next part of the operation, & 255, uses a bitwise AND to create a new integer which is capped to 255 (the first 8 bits). This effectively truncates the “red” part of the remaining color resulting in 105 - the green part of our color; see Figure 5-21 for an illustration of this operation.
A315054_1_En_5_Fig20_HTML.jpg
Figure 5-20.
Illustration of a Bitwise Sign-Propagating Right Shift of 8 against 16738740
A315054_1_En_5_Fig21_HTML.jpg
Figure 5-21.
Illustration of a Bitwise AND Operation of 65535 against 255
b: hex & 255
To get the blue value, you can simply use the bitwise AND operator to extract the first 8 bits (225). The bitwise AND operator would effectively truncate the “red” and “green” parts of the remaining color. So assuming the hex was 0xFF69B4 (16738740), the result would be 180, the blue color. See Figure 5-22 for an illustration of this operation.
A315054_1_En_5_Fig22_HTML.jpg
Figure 5-22.
Illustration of a Bitwise AND Operation of 16738740 against 255
So, by using a mixture of the bitwise sign-propagating right shift operator (you could alternatively use the zero-fill right shift operator, it would be the same result as RGB colors are positive integers) and the bitwise AND operator with 255 as the operand, you can extract each color component and separate out the hexadecimal value into three distinct color values.

RGB2Hex

function rgb2hex(r, g, b) {
rgb2hex is a more complex function, but uses the same techniques as hex2rgb. The function declaration (you can read more about functions in Chapter 12) requires three arguments named r, g, and b, respectively. We will pass it three numbers for each call—a red, a green, and a blue value. In this example, we’ll pass it the RGB of 255, 105, and 180 (the CSS color “hot pink” has a hex value of 0xFF69B4).
return ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);
This line looks complex, but is actually reasonably simple and is much simpler than Hex2RGB. The first part, (1 << 24) is a bit of a trick: it sets up the resulting integer to be a 25-bit integer. Each color section (red, green, and blue) is 8 bits, and 8 x 3 = 24, which means a full RGB color code is 24-bits, but it is calculated here to be 25 bits long because if the red value was 0 it would be truncated in the resulting integer (evaluated numbers in JavaScript have no leading 0s). By making the integer 25 bits (with a leading 1), we can truncate the MSB later but still maintain the six remaining hexadecimal colors. To give you an example of this, if the color was black (0,0,0), then the resulting Integer would be 0 (all leading 0s are removed), which would result in a hex of 0 when it should be 000000. The resulting value of (1 << 24) is 16777216, or a binary value of 0b1000000000000000000000000.
The next part, + (r << 16) shifts the red value by 16 bits to the left and adds this to the resulting integer. See Figure 5-23 for an illustration of this. Then + (g << 8) shifts the green value by 8 bits to the left, adding it to the integer as illustrated in Figure 5-24. Finally, as part of this integer operation, the blue value (b) is added. This whole integer—assuming an RGB of 255, 105, 180—would total 33515956 (0b1111111110110100110110100 in binary notation).
A315054_1_En_5_Fig23_HTML.jpg
Figure 5-23.
Illustration of a bitwise sign-propagating right shift of 16 against 16777215
This entire integer is not converted to a 16-bit string using the toString(16) function, which converts the Base10 integer to a Base16 integer (this functionality is discussed in Chapter 4). Assuming our 255, 105, 180 values, this hex at this point would equal ”1FF69B4”.
The final.slice(1) function call slices off the string’s first character (slice is discussed in more detail in Chapter 3), which makes the resulting string ”FF69B4” in our example.

Creating, Editing, and Testing a Bit Field Flag Set Using Bitwise Operators

Problem

You want to create or alter a bit field (which is a binary integer made up of a set of smaller integers, known as flags; popular examples are CHMOD permissions ) using bitwise operators to accurately toggle or check values.

Solution

Bit fields make extensive use of bitwise operators to add, remove, toggle, and check flags in the field.

The Code

var execute = 1 << 0,   // 001 or 1
    write           = 1 << 1,   // 010 or 2
    read        = 1 << 2;   // 100 or 4
function removePermission(permission, flag) {
    return permission & ∼flag;
}
function addPermission(permission, flag) {
    return permission | flag;
}
function togglePermission (permission, flag) {
    return permission ^ flag;
}
function permissionToString(permission) {
    var stringPermission = '';
    stringPermission += (permission & read) ? 'r' : '-';
    stringPermission += (permission & write) ? 'w' : '-';
    stringPermission += (permission & execute) ? 'x' : '-';
    return stringPermission;
}
var writeExecute = write | execute,           // 011 or 3
    readExecute   = read | execute,           // 101 or 5
    readWrite     = read | write,             // 110 or 6
    full          = read | write | execute;   // 111 or 7
console.log( permissionToString(1) ); // —x
console.log( permissionToString(2) ); // -w-
console.log( permissionToString(3) ); // -wx
console.log( permissionToString(4) ); // r—
console.log( permissionToString(5) ); // r-x
console.log( permissionToString(6) ); // rw-
console.log( permissionToString(7) ); // rwx
console.log( permissionToString( removePermission(7, read) ) ); // -wx
console.log( permissionToString( removePermission(7, write) ) ); // r-x
console.log( permissionToString( removePermission(7, execute) ) ); // rw-
console.log( permissionToString( addPermission(6, execute) ) ); // rwx
console.log( permissionToString( removePermission(7, execute) ) ); // rw-
console.log( permissionToString( togglePermission(6, execute) ) ); // rwx
console.log( permissionToString( togglePermission(7, execute) ) ); // rw-
Listing 5-10.
Creating, Editing, and Testing a Bit Field Flag Set Using Bitwise Operators
The output is:
--x
-w-
-wx
r--
r-x
rw-
rwx
-wx
r-x
rw-
rwx
rw-
rwx
rw-

How It Works

Bit fields are a popular way to establish a command set for low-level protocols. They exist in transport protocols, binary files, and even file systems, and quite rightly so—they are an elegant and compact way to express a great deal of information in a short manner.
By using a combination of many of the bitwise operators , flags can be used to alter a bit field or check existing values. This section shows nothing new, just a combination and application of techniques throughout this chapter, applied to a well-known and commonly used bit field—a CHMOD permissions table. The CHMOD permissions table uses three flags for Read, Write, and Execute access on a file in Linux/UNIX (including Mac OSX) file systems. In these file systems, permission for a file is made of three octets (0-7)—one for the user, one for the Group, and one for “other” (everyone else). Each of these octets represents a bit field of Read, Write, and Execute values; see Table 5-3 for how this works.
Table 5-3.
Bitwise Operator Functions on Bit Fields
Permission
Bit Indicator
Numerical Equivalent
Read
X00
4
Write
0X0
2
Execute
00X
1
Because of the way binary integers work, this bit field can only have a maximum number of values, and there are no ambiguous values. This means any combination can be used and none overlap. See Table 5-4 for all possible values.
Table 5-4.
Bitwise Operator Functions on Bit Fields
Numerical Equivalent
Binary
Permission
0
0b000
None
1
0b001
Execute
2
0b010
Write
3
0b011
Write + Execute
4
0b100
Read
5
0b101
Read + Execute
6
0b110
Read + Write
7
0b111
Read + Write + Execute
This effectively means we can do low-level bit manipulation to check or alter the flags inside the bit field, as demonstrated in the code. Primarily the operators in use are the binary left-shift, NOT, XOR, AND, and OR operators. Each operator has its own purpose in bit fields, as illustrated in Table 5-5.
Table 5-5.
Bitwise Operator Functions on Bit Fields
Bitwise Operator
Bit Field Function
Bitwise Shift (<<, >>)
Creating flag values
Bitwise OR operator
Adding flag values to a bit field
Bitwise NOT operator
Removing flag values from a bit field
Bitwise XOR operator
Toggling flag values on a bit field
Bitwise AND operator
Checking flag existence on a bit field
Refer to the previous sections of this chapter to learn more about each of these bitwise operators, in combination they can be used to perform powerful low-level operations on numerical or binary data.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.14.196