Compound Boolean Expressions
Compound Boolean Expressions
int main() {
double d1 = 1.11 - 1.10,
d2 = 2.11 - 2.10;
cout << "d1 = " << d1 << endl;
cout << "d2 = " << d2 << endl;
if (d1 == d2)
cout << "Same" << endl;
else
cout << "Different" << endl;
cout << "d1 = " << setprecision(20) << d1 << endl;
cout << "d2 = " << setprecision(20) << d2 << endl;
}
In Listing 5.5 (samedifferent.cpp) the displayed values of d1 and d2 are rounded so they appear equivalent,
but internally the exact representations are slightly different. By including the header iomanip we can
use the setprecision stream manipulator to force cout to display more decimal places in the floating-
point number it prints. Observe from the output of Listing 5.5 (samedifferent.cpp) that the two quantities
that should be identically 0.01 are actually slightly different.
d1 = 0.01
d2 = 0.01
Different
d1 = 0.010000000000000009
d2 = 0.0099999999999997868
This result should not discourage you from using floating-point numbers where they truly are needed.
In Section 9.4.6 we will see how to handle floating-point comparisons properly.
Simple Boolean expressions, each involving one relational operator, can be combined into more complex
Boolean expressions using the logical operators && (and), || (or), and ! (not). A combination of two or
more Boolean expressions using logical operators is called a compound Boolean expression.
To introduce compound Boolean expressions, consider a computer science degree that requires, among
other computing courses, Operating Systems and Programming Languages. If we isolate those two courses,
we can say a student must successfully complete both Operating Systems and Programming Languages to
qualify for the degree. A student that passes Operating Systems but not Programming Languages will not
have met the requirements. Similarly, Programming Languages without Operating Systems is insufficient,
and a student completing neither Operating Systems nor Programming Languages surely does not qualify.
Logical AND works in exactly the same way. If e1 and e2 are two Boolean expressions, e1 && e2 is true
only if e1 and e2 are both true; if either one is false or both are false, the compound expression is false.
To illustrate logical OR, consider two mathematics courses, Differential Equations and Linear Algebra.
A computer science degree requires one of those two courses. A student who successfully completes
Differential Equations but does not take Linear Algebra meets the requirement. Similarly, a student may
take Linear Algebra but not Differential Equations. It is important to note the a student may elect to take
e1 e2 e1 && e2 e1 || e2 !e1
false false false false true
false true false true true
true false false true false
true true true true false
both Differential Equations and Linear Algebra (perhaps on the way to a mathematics minor), but the
requirement is no less fulfilled.
Logical OR works in a similar fashion. Given our Boolean expressions e1 and e2 , the compound ex-
pression e1 || e2 is false only if e1 and e2 are both false; if either one is true or both are true, the compound
expression is true. Note that logical OR is an inclusive or, not an exclusive or. In informal conversion we
often imply exclusive or in a statement like “Would you like cake or ice cream for dessert?” The implica-
tion is one or the other, not both. In computer programming the or is inclusive; if both subexpressions in
an or expression are true, the or expression is true.
Logical NOT simply reverses the truth value of the expression to which it is applied. If e is a true
Boolean expression, !e is false; if e is false, !e is true.
Table 5.3 is called a truth table. It shows all the combinations of truth values for two simple expres-
sions and the values of compound Boolean expressions built from applying the &&, ||, and ! C++ logical
operators.
Both && and || are binary operators; that is, they require two operands, both of which must be Boolean
expressions. Logical not (!) is a unary operator (see Section 4.1); it requires a single Boolean operand
immediately to its right.
Operator ! has higher precedence than both && and ||. && has higher precedence than ||. && and ||
are left associative; ! is right associative. && and || have lower precedence than any other binary operator
except assignment. This means the expression
is evaluated
Some programmers prefer to use the parentheses as shown here even though they are not required. The
parentheses improve the readability of complex expressions, and the compiled code is no less efficient.
The relational operators such as < compare two operands. The result of the com-
parison is a Boolean value, which is freely convertible to an integer. The misappli-
cation of relational operators can lead to surprising results; consider, for example,
the expression
1 <= x <= 10
This expression is always true, regardless of the value of x! If the programmer’s
intent is to represent the mathematical notion of x falling within the range 1...10
inclusive, as in 1 ≤ x ≤ 10, the above C++ expression is not equivalent.
The expression
1 <= x <= 10
is evaluated as
(1 <= x) <= 10
If x is greater than or equal to one, the subexpression 1 ∼ <= ∼ x evaluates to
true, or integer 1. Integer 1, however, is always less than 10, so the overall expres-
sion is true. If instead x is less than one, the subexpression 1 ∼ <= ∼ x evaluates
to false, or integer 0. Integer 0 is always less than 10, so the overall expression is
true. The problem is due to the fact that C++ does not strictly distinguish between
Boolean and integer values.
!(x == y)
(x < y || x > y)
In the expression e1 && e2 both subexpressions e1 and e2 must be true for the overall expression to be
true. Since the && operator evaluates left to right, this means that if e1 is false, there is no need to evaluate
e2 . If e1 is false, no value of e2 can make the expression e1 && e2 true. The logical and operator first tests the
expression to its left. If it finds the expression to be false, it does not bother to check the right expression.
This approach is called short-circuit evaluation. In a similar fashion, in the expression e1 || e2 , if e1 is
true, then it does not matter what value e2 has—a logical or expression is true unless both subexpressions
are false. The || operator uses short-circuit evaluation also.
Why is short-circuit evaluation important? Two situations show why it is important to consider:
• The order of the subexpressions can affect performance. When a program is running, complex ex-
pressions require more time for the computer to evaluate than simpler expressions. We classify an
expression that takes a relatively long time to evaluate as an expensive expression. If a compound
Boolean expression is made up of an expensive Boolean subexpression and an less expensive Boolean
subexpression, and the order of evaluation of the two expressions does not affect the behavior of the
program, then place the more expensive Boolean expression second. If the first subexpression is false
and && is being used, then the expensive second subexpression is not evaluated; if the first subex-
pression is true and || is being used, then, again, the expensive second subexpression is avoided.
• Subexpressions can be ordered to prevent run-time errors. This is especially true when one of the
subexpressions depends on the other in some way. Consider the following expression:
Here, if x is zero, the division by zero is avoided. If the subexpressions were switched, a run-time
error would result if x is zero.
The statements in the body of the if or the else may be any C++ statements, including other if/else
statements. We can use nested if statements to build arbitrarily complex control flow logic. Consider
Listing 5.6 (checkrange.cpp) that determines if a number is between 0 and 10, inclusive.