It is easy to see what each of these operators does by using truth tables. To illustrate how truth tables work, consider the algorithm for binary addition. In Section 3.1 we saw that the \(i^{th}\) bit in the result is the sum of the \(i^{th}\) bit of one number plus the \(i^{th}\) bit of the other number plus the carry produced from adding the \((i - 1)^{th}\) bits. This sum will produce a carry of zero or one. In other words, a bit adder has three inputs—the two corresponding bits from the two numbers being added and the carry from the previous bit addition, and two outputs—the result and the carry.
In a truth table we have a column for each input and each output. We write down all possible input bit combinations and then show the output(s) in the corresponding row. A truth table for the bit-by-bit addition of z = x + y is shown in Table 4.4.1. We use the notation x[i] to represent the \(i^{th}\) bit in the variable x.
Table4.4.1.Truth table for the bit-by-bit z = x + y operation. x[i] is the \(i^{th}\) bit of x; carry[i-1] is the carry from adding the \((i - 1)^{th}\) bits.
inputs
outputs
x[i]
y[i]
carry[(i-1)]
z[i]
carry[i]
\(\binary{0}\)
\(\binary{0}\)
\(\binary{0}\)
\(\binary{0}\)
\(\binary{0}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{0}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{0}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{0}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{0}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{1}\)
The bitwise logical operators act on the corresponding bits of two operands as shown in Table 4.4.2–Table 4.4.5.
Table4.4.2.and
x[i]
y[i]
x[i] & y[i]
\(\binary{0}\)
\(\binary{0}\)
\(\binary{0}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{0}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{1}\)
Table4.4.3.inclusive or
x[i]
y[i]
x[i] | y[i]
\(\binary{0}\)
\(\binary{0}\)
\(\binary{0}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{1}\)
Table4.4.4.exclusive or
x[i]
y[i]
x[i] ^ y[i]
\(\binary{0}\)
\(\binary{0}\)
\(\binary{0}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{0}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{0}\)
Table4.4.5.complement
x[i]
~x[i]
\(\binary{0}\)
\(\binary{1}\)
\(\binary{1}\)
\(\binary{0}\)
Make sure that you distinguish these bitwise logical operators from the C/C++ logical operators, &&, ||, and !. The logical operators work on groups of bits organized into integral data types rather than individual bits. For comparison, the truth tables for the C/C++ logical operators are shown in Table 4.4.6–Table 4.4.8.
Table4.4.6.and
x
y
x && y
\(\binary{0}\)
\(\binary{0}\)
\(\binary{0}\)
\(\binary{0}\)
non-zero
\(\binary{0}\)
non-zero
\(\binary{0}\)
\(\binary{0}\)
non-zero
non-zero
\(\binary{1}\)
Table4.4.7.or
x
y
x || y
\(\binary{0}\)
\(\binary{0}\)
\(\binary{0}\)
\(\binary{0}\)
non-zero
\(\binary{1}\)
non-zero
\(\binary{0}\)
\(\binary{1}\)
non-zero
non-zero
\(\binary{1}\)
Table4.4.8.complement
x
!x
\(\binary{0}\)
\(\binary{1}\)
non-zero
\(\binary{0}\)
Now we are prepared to see how we can convert from the ASCII character code to the int format. The & operator works very nicely for the conversion. First, look at the comparison between each hexadecimal character and its corresponding integer value in Table 4.4.9.
Table4.4.9.Hexadecimal characters and corresponding int
Hex character
ASCII code
Corresponding int
\(\hex{0}\)
\(\hex{30}\)
\(\hex{0000 0000}\)
\(\hex{1}\)
\(\hex{31}\)
\(\hex{0000 0001}\)
\(\hex{2}\)
\(\hex{32}\)
\(\hex{0000 0002}\)
\(\hex{3}\)
\(\hex{33}\)
\(\hex{0000 0003}\)
\(\hex{4}\)
\(\hex{34}\)
\(\hex{0000 0004}\)
\(\hex{5}\)
\(\hex{35}\)
\(\hex{0000 0005}\)
\(\hex{6}\)
\(\hex{36}\)
\(\hex{0000 0006}\)
\(\hex{7}\)
\(\hex{37}\)
\(\hex{0000 0007}\)
\(\hex{8}\)
\(\hex{38}\)
\(\hex{0000 0008}\)
\(\hex{9}\)
\(\hex{39}\)
\(\hex{0000 0009}\)
\(\hex{a}\)
\(\hex{61}\)
\(\hex{0000 000a}\)
\(\hex{b}\)
\(\hex{62}\)
\(\hex{0000 000b}\)
\(\hex{c}\)
\(\hex{63}\)
\(\hex{0000 000c}\)
\(\hex{d}\)
\(\hex{64}\)
\(\hex{0000 000d}\)
\(\hex{e}\)
\(\hex{65}\)
\(\hex{0000 000e}\)
\(\hex{f}\)
\(\hex{66}\)
\(\hex{0000 000f}\)
For the characters \(\hex{0}\)—\(\hex{9}\) the conversion from the ASCII value to an int value can be easily done with the code:
aChar = aChar & 0x0f;
anInt = (int)aChar;
where aChar is a char variable and anInt is an int variable. The & operation used this way is often called masking. The (int) operation type casts the value stored in aChar to be an int value, effectively extending it to be 32 bits.