 .NET 1.1+C# Logical Bitwise Operators
The ninth part of the C# Fundamentals tutorial extends upon the previous article dealing with C# Boolean operators. Boolean operations may also be carried out on integer representations of binary numbers. This article considers logical bitwise operators.
Bitwise Logic and Binary
In the previous part of the C# Fundamentals tutorial I described the Boolean operators and their effect on Boolean data type values. The bitwise logic operators provide the same logical AND, OR and XOR functions for operation on each bit of integer values. This can be very useful for interrogating the value of individual bits from a binary stream of integers or bit fields, particularly when receiving information from Input / Output devices.
For developers who are new to interrogating individual bits in bit fields, consider the following example. Let us assume that we have a device connected to a serial port that indicates its status using a single byte. This byte is comprised of eight binary digits or bits, each of which provides some simple information. The following table describes the use of each bit. For clarity I have indicated the value that the byte would hold if it were a signed byte data type value with only one bit set.
| Bit | Bit Value | Meaning (1) | Meaning (0) |
|---|
| 7 | 128 | Ready | Off-Line | | 6 | 64 | Connected | Not Connected | | 5 | 32 | Carrier Present | Carrier Absent | | 4 | 16 | Log Data | Do Not Log Data | | 3 | 8 | Auto Answer Mode | Manual Answer Mode | | 2 | 4 | Echo Commands | Do Not Echo Commands | | 1 | 2 | Use 8 Data Bits | Use 7 Data Bits | | 0 | 1 | Use Odd Parity | Use Even Parity |
Reading from our example device may yield a signed byte result of 187 or 10111011 in binary. Comparing this value to our table we can see that the device is ready, not connected and has a carrier signal present. It is set to log data and answer automatically. It is not echoing commands and is using eight data bits and odd parity.
This is a lot of information to hold in or extract from a single byte of information. The following sections explain how to use the bitwise operators to read and process this type of information.
Bitwise AND Operator
The bitwise AND operator is generally used to test the values of bits within a larger integer value. For our example device's output we may want to isolate the value of bit 5 to determine if a carrier signal is present or not. By using the AND operator with the value 32, the resultant value is our original value of 187 with every bit set to zero by the operation. In other words, the only possible set bit that can remain is bit 5. A simple diagram showing the binary and decimal representations will clarify this.
10111011 = 187
00100000 = 32
AND
00100000 = 32
As you can see, each binary digit pair is affected individually by the AND operation. Only bit 5 can possibly remain set in our result. This is the key to reading isolated bits using a bitwise AND. For a single bit, if the resultant value is zero then the bit was not set. If the result is any other value then the tested bit must have been set.
The AND operation can also be used to clear bits in an integer value, ie. set some bits to zero. For example, to clear bit 5 from the configuration byte, we can perform an AND operation with a value with bit 5 not set. Again, a binary representation will help to clarify this.
10111011 = 187
11011111 = 223
AND
10011011 = 155
To achieve a bitwise AND operation, the operator used is the ampersand (&). The following examples illustrate reading an isolated bit from an integer value.
int deviceConfiguration = 187; // 10111011
int carrierMask = 32; // 00100000
int connectedMask = 64; // 01000000
// Check the carrier
int carrier = deviceConfiguration & carrierMask; // Result = 32
// Check the connection status
int connected = deviceConfiguration & connectedMask; // Result = 0
Bitwise OR OperatorThe bitwise OR operator is generally used to set bits within a larger integer value, ie. set some bits to one. For our example device, we may want to set bit 2 to indicate that we want to echo commands. Let's look at the binary OR operation first.
10111011 = 187
00000100 = 4
OR
10111111 = 191
Each binary digit pair is affected individually by the OR operation. Wherever the second operand has a zero bit, the bit from the first operand is copied into the final result. However, if we set a one bit this ensures the matching resultant bit will be set to one.
To achieve a bitwise OR operation, the operator used is the bar character (|). The following example illustrates setting bits in an integer value.
int deviceConfiguration = 187; // 10111011
int echoMask = 4; // 00000100
// Set the carrier
int newConfiguration = deviceConfiguration | echoMask; // Result = 191
Bitwise XOR Operator
The bitwise exclusive OR (XOR) operator allows the programmer to toggle specific bits between their possible values of zero or one. A common use of the XOR bitwise is to provide basic reversible encryption of data. As the bitwise exclusive OR toggles some bits, performing the operation twice restores the original values. With a long key for encryption, this can be quite effective (though inadvisable for data that needs to be kept very secure.) See below for an example of an exclusive OR operation.
10111011 = 187
01010101 = 85
XOR
11101110 = 238
Each binary digit pair is affected individually by the XOR operation. Wherever the second operand has a zero bit, the corresponding bit from the first operand is copied into the resultant value. However, if we set a one bit this toggles the bit in the result.
To achieve a bitwise XOR operation, the operator used is the caret character (^). The following example illustrates toggling bits in an integer value to encode and then decode data.
int valueToEncode = 187; // 10111011
int keyMask = 85; // 01010101
// Encode
int encoded = valueToEncode ^ keyMask ; // Result = 238
// Decode
int decoded = encoded ^ keyMask; // Result = 187
One's Complement (NOT) Operator
The final basic logical bitwise operator provides a NOT function similar to that of the Boolean NOT operator but again performed on each individual bit. This is also known as a one's complement operation. The binary example is as follows:
10111011 = 187
NOT
01000100 = 68
The bitwise NOT operator is a unary operator as it only requires a single
operand. Unlike the other bitwise operators mentioned above, the bitwise version
does not use the same symbol as the similar Boolean operator. To achieve a one's complement,
the tilde character (~) is positioned to the left of the value to modify.
byte valueToComplement = 187; // 10111011
byte complement = (byte) ~valueToComplement; // Result = 68
In the above example you can see that the result of the NOT operation has been cast to a byte data type. This is because the operator returns an integer value that cannot be assigned directly into a byte. Note also that I have used an unsigned byte. In a signed byte the highest order bit is used to determine if the value is negative and the remaining bits are adjusted for negative numbers using two's complement notation. Using signed values can therefore lead to some accurate but tricky to decipher results.
Compound Assignment Operators
In part seven of the C# Fundamentals tutorial I introduced the compound assignment operators for arithmetic functions. These operators allow the use of an arithmetic operator to modify an existing variable. The same type of compound operator exists for the bitwise AND, OR and XOR operators.
The syntax of the compound assignment operators is identical in nature to that of the arithmetic variety. Some examples follow.
// Initialise
byte value = 240; // 11110000
// Clear bit 7
value &= 127; // Result = 112 = 01110000
// Set bit 0
value |= 1; // Result = 113 = 01110001
// Toggle bits 1, 3, 5 and 7
value ^= 170; // Result = 219 = 11011011
Operator Precedence
We can now extend the operator precedence table further using the new operators discussed in this article.
| Parentheses Operator |
|---|
| () | | Increment / Decrement Operators |
|---|
| ++(postfix) --(postfix) ++(prefix) --(prefix) | | Complement Operators |
|---|
| ! ~ | | Basic Arithmetic Operators |
|---|
| * / % + - | | Equivalence Operators |
|---|
| == != | | Logic / Bitwise Operators |
|---|
| & ^ | && || | | Assignment Operator |
|---|
| = | | Compound Assignment Operators |
|---|
| *= /= %= += -= &= ^= |= |
|