In previous topics, you worked with mathematical operators. This should not have been too difficult since you have been working with most of these operators for quite a while. Your next step in this chapter is to start working with a different group of operators that compare one value or quantity to another. The processes you will learn about are related to changing the actions your program takes as a result of these comparisons. The comparison and evaluation actions themselves are simple, and obviously logical, but it gets more interesting when you combine one set of decision making processes with others. Once you have learned about the logical comparisons and operations, you will have made a huge leap forward in your ability to create powerful programs.
Conditional Operators and Operations
Before you can make program decisions, you must be able to use the relational operators. These are the operators that will help you compare data values in various ways, such as greater than, equal, or less than. There are six of these operators that are most commonly used for comparison of values in a C++ program. They are shown below.
|==||equality||if the thing on the left side has a value equal to the thing on the right side, evaluate to true, evaulate to false|
|>||greater than||if the thing on the left side has a value greater than the thing on the right side, evaluate to true, evaluate to false|
|<||less than||if the thing on the left side has a value less than the the thing on the right side, evaluate to true, else evaluate to false|
|>=||greater than or equal to||if the thing on the left side has a value that is greater than or equal to the thing on the right side, evaluate to true, else evaluate to false|
|<=||less than or equal to||if the thing on the left side has a value that is less than or equal to the thing on the right side, evaluate to true, else evaluate to false|
|!=||not equal||if the thing on the left side has a value that is not equal to the thing on the right side, evaluate to true, else evaluate to false|
The operators shown are considered binary operators because they require two parameters to be used, as shown below.
a < b
The result of any of these expressions are either true or false. Given the condition that a was numerically smaller than b, this expression would evaluate to true. If a were larger than or equal to b, the expression would evaluate to false. As a note, always remember that the opposite of "less than" is not just "greater than"; the opposite of "less than" is "greater than or equal".
Another example is shown below.
a == b
This expression would evaluate to true if a were equal to b. Otherwise it would evaluate to false.
Another expression that is exactly the opposite of a == b is the following.
a != b
This expression would evaluate to true if a were not equal to b. You will look at the NOT operator soon, but the "not equal" operator is one of the relational operators.
As you recall from previous topics, the fundamental data types you have worked with so far are int, double, and char. For logical operations, you will be using another fundamental data type called a Boolean, or the bool data type. The bool data type is simple in that it only has two values, which are true and false. This data type is used quite a bit for storing the results of relational operations, and decision making conditions. For that reason, you will want to become comfortable with it before you go on. The next step then, is to show that since you can hold the values true and false in a bool variable, you can also assign the results of a relational expression to a bool variable, as shown below.
bool relationalResult; relationalResult = a == b;
Your expression assignment will work with any of the relational operators, as shown here.
relationalResult_1 = a > b; relationalResult_2 = b < c;
Now the first thing you notice is that your program can choose to implement either the assignment operation first, or the relational operator first. As you learned earlier, the assignment operator's operational priority is the lowest, so the relational expression will be evaluated first. This means that in the statements above, the relational expression will be evaluated first, and then the results of the evaluation will be assigned to the Boolean variable relationalResult. However, it is almost always better style and programming to place parentheses around the relational expression, just to be clear to another programmer what variables are being compared, and what result is being assigned. Better expression implementations are shown here.
relationalResult_1 = ( a > b ); relationalResult_2 = ( b < c );
As far as order of operations go, the relational operators have equal weight. In other words, if an expression has a series of relational operators, the expression will be evaluated left to right. The good news about evaluating a series of several relational operators in a row is that you can easily save yourself from having to figure out what operator is doing what by implementing a simple plan - do not do it. It is poor Computer Science, and it is not very good programming practice. Shown below are examples of good and bad. It is legal, but poor practice to implement the expression shown here.
a < b < c; // this is actually evaluated as // ( a < b ) < c // which is not very useful
This single expression should be expanded to the pair of expressions shown here.
a < b; b < c;
The bottom line is that whether you assign a Boolean expression to a variable, or use it in any other way, your program will be most interested in the result of your process. As an example, if the expression A < B is used in some way, and A is assigned four, and B is assigned 7, the computer will "see" true (i.e., not "A < B"), and if you have assigned this quantity to a variable, it will assign the value true. You will see later that you are only interested in what the expression becomes (i.e., true or false), not what is actually in it. Once again, you will be looking at abstracted conditions meaning that you want to always check for the relationship between the first item and the next no matter what values might be used in the given variables. You will next look at logical operations, and you will find that the Boolean analysis process can easily be implemented with these operators.
There are three pairs of operators that help make your logical choices a little easier to analyze. They are shown here.
The first thing to note is that you have two ways to implement the three logical processes. When C++ first came out, it was decided to include the use of English words as operators since not all keyboards or character sets around the world could support the three operators (i.e., the "&&", the "||", and the "!"). By providing English words instead of the given characters, programs could be developed in several different computer systems internationally, if needed.
The AND logical rule
The AND operator requires that any two or more quantities must all be true for the whole expression to evaluate to true.
A simple example is shown here.
bool a, b, c; a = true; b = true; c = ( a && b );
In the example above, the expression between the parentheses evaluates to true, so the variable c now holds the value true. On the other hand, the following shows when an expression will not evaluate to true.
bool a, b, c; a = true; b = false; c = ( a && b );
In the example above, the expression between the parentheses evaluates to false, so the bool variable c now holds the value false. The AND operator requires that both (or all, if more than two) values must be true. Shown next is one more expression with more than two bool values.
bool a, b, c, d, e, f; a = true; b = false; c = true; d = true; e = true; f = ( a && b && c && d && e );
In this last example, as you can see, the bool variable f will hold the value false since one of the bool terms in the expression, b, is false.
The OR logical rule
The OR operator requires that at least one of the two or more terms be true for the expression to evaluate to true.
Here is an example.
bool a, b, c; a = false; b = true; c = ( a || b );
In the example above, the variable c now holds the value true. On the other hand, the following shows when an expression will not evaluate to true.
bool a, b, c; a = false; b = false; c = ( a || b );
Once again in the example above, the bool variable c holds the value false. In addition, if an expression has many terms, it only takes one to make the whole expression evaluate to true. This is demonstrated here.
bool a, b, c, d, e, f; a = false; b = false; c = true; d = false; e = false; f = ( a || b || c || d || e );
Because one of the terms was true, the whole expression evaluated to true, and the bool variable f will now hold the value true.
The NOT logical rule
Logically speaking, there are only two ways to see the world. A logical result can either be true or false. However, logically speaking again, the world can be made even simpler by saying that the two results of a logical operation can be true or not true (or false and not false). The NOT operator extends the flexibility of logical analyses so that expressions can be simplified, or made clearer in your programs.
As a matter of fact, the C++ programming language uses this in its analysis of logical comparisons. When an expression is tested with any of the relational operators, if the process yields a zero, the result is considered false. If the process yields any other number (i.e., NOT zero), the result is considered to be true. It is legal to use the values zero and one as false and true respectively, but it is not good Computer Science.
Once you have abstracted a quantity into a useful tool, such as developing a data type that holds true and false, you should stick with the abstraction. Going back and forth between the abstraction and the things it represents rapidly loses the higher level problem solving processes that you are reaching for.
Shown next are a few examples of using the NOT operator.
bool a, b, c; a = false; b = false; c = !( a || b );
In the example above, the bool variable c will hold the value true. This is because the expression inside the parentheses evaluates to false (false OR false evaluates to false). Then the NOT operator changes the result to the opposite of what it was, which makes the expression result in true. Another example is shown below.
bool a, b, c; a = false; b = true; c = !( a && b );
In the example above, the expression will also evaluate to true. The expression a AND b (e.g., false AND true) evaluates to false, and the NOT operator switches it to true.
One more example is shown below.
bool a, b, c; a = true; b = true; c = !( a && b );
In the example above, the expression will evaluate to false. The expression a AND b (e.g., true AND true) evaluates to true, and the NOT operator switches it to false. In a nutshell, the NOT operator takes whatever expression or term it is in front of and switches it to the opposite of what it was.
The NOT operator can also be used with an equality test operation to emulate the not equal operator (i.e., "!=") you read about earlier in this topic. This is conducted as shown here.
bool testForNotBeingZero = !( x == 0 );
is the same as:
bool testForNotBeingZero = ( x != 0 );
While these are both exactly the same operation, there are times that one of them might be clearer to use than the other. As usual, watch for the opportunities to make your program more readable.
One more note about the use of parentheses. If there is ever a question about what you meant to do with an expression, use parentheses to show your intentions. There will be many times that they will not be necessary since the order of operations rules will take care of things for you. Remember (again) that you are not just writing a program to work; you are writing it to be reviewed, maintained, and potentially upgraded.
Using parentheses to make your intentions clear in a program is one of the easiest ways to minimize incorrect expression results. The computer will not make mistakes by implementing your expression differently than you meant to, and your fellow programmers will not make mistakes misinterpreting your code.
Application of the rules
Now, the next thing to do is to demonstrate some examples of the usage of the logical operators. Here are a couple of examples that will help you understand the usage of these operators.
Consider the following conditions related to a student.
bool tall = true; bool athleticallyInclined = true; bool interestedInBasektball = true; bool givesExtraEffort = true; bool femaleStudent = true; bool shootsFromDistance = false; bool fastRunningTimes = true; bool goodRebounder = true; bool goodBasketballDribbler = true; // either qualification will work for the needed value capableOfPlayingBasketball = tall || athleticallyInclined; // requires both capable and female qualifiedForPlayingWomensBasketball = capableOfPlayingBasketball && femaleStudent; // requires both qualified and extra effort QualifiedToMakeTheWomensBasketballTeam = qualifiedForWomensBasketball && givesExtraEffort;
Now, notice that this statement could have been made as follows.
// covers all the conditions, // but may get too complicated in one statement QualifiedToMakeTheWomensBasketballTeam = ( tall || athleticallyInclined ) && femaleStudent && givesExtraEffort;
Here are a couple more examples using this system.
// combination of conditions required capableOfPlayingGuard = ( shootsFromDistance || fastRunningTimes ) && goodBasketballDribbler; // both necessary for successful center capableOfPlayingCenter = tall && goodRebounder;
Here is another example.
bool short = true; bool maleStudent = true; bool athleticallyInclined = true; bool interestedInBaseball = true; bool strongThrowingArm = false; bool goodCatchingSkills = true; bool goodBattingAverage = true; bool fastRunningTimes = true; bool givesExtraEffort = true; // usually one or the other will get someone // started in baseball // note the use of the NOT operator // on the individual bool variable capableOfPlayingBaseball = !short || athleticallyInclined; // requirement for being a pitcher capableOfPitching = capableOfPlayingBaseball && strongThrowingArm; // requirements for playing a base capableOfPlayingABase = capableOfPlayingBaseball && ( GoodCatchingSkills || GoodThrowingArm ); // requirements for being a pinch hitter capableOfPlayingPinchHitter = goodBattingAverage && fastRunningTimes; // requirements for making the team qualifiedToMakeTheMensBaseballTeam = ( capableOfPitching || capableOfPlayingABase || capableOfPlayingOutfield || capableOfPlayingPinchHitter ) && givesExtraEffort && maleStudent; qualifiedToBeTheWaterCarrier = !qualifiedToMakeTheMensBaseballTeam;
Calculating for years
There are several ways to use these logical operators, and you will see many of them in the remainder of this course. Here is another, more mathematically oriented use of the logical and relational operators together. To qualify as a leap year, a year must be divisible by four, with the exception that every fourth century is a leap year (and the other three centuries are not).
In other words, the years 1700, 1800, and 1900 were not leap years, but the year 2000 was. All are divisible by four, but since only every fourth century is a leap year, you need to accept years that are divisible by four and not divisible by 100, or accept divisible by 400. Think about how you would put this into logical terms before looking at the example shown below.
isLeapYear = ( year % 4 == 0 ) && ( year % 100 != 0 ) || ( year % 400 == 0 );
This may be a little hard to analyze just like it is, so break it down as shown below. Readability is always an important goal.
divisibleByFour = ( year % 4 == 0 ); notAnOddCentury = ( year % 100 != 0 ); isAFourthCentury = ( year % 400 == 0 ); isLeapYear = divisibleByFour && notAnOddCentury || isAFourthCentury;
Before you jump into the rest of the analysis, note how the modulo operator is being used. The testing process of divisibleByFour simply asks "does the year divide evenly by four (i.e., is the remainder of the division zero)?". The notAnOddCentury test asks "does the year NOT divide by 100 (i.e., is the remainder of division by 100 not equal to zero)?".
Finally, isAFourthCentury asks the question "is the year divisible by 400 (i.e., is the remainder zero when divided by 400)?". All these are powerful examples of using the modulo operator for control and decision making conditions. This is another handy item to place in your programming toolbox.
The next thing to note is the order of operations. Relational operators all have the same priority, so the expression above might have been evaluated from left to right. However, the logical operators have a precedence as follows: (not or "!" first; and "&&" second; and "||" third).
Since the first two Boolean variables are ANDed, the AND process will happen first. If that process fails to produce a true result, then the other item (i.e., isAFourthCentury) will be evaluated on its own. Since the OR is between the first two items and the last one, if isAFourthCentury is true, the expression will resolve to true.
Consider the results if the expression had been as shown below
isLeapYear = isAFourthCentury || divisibleByFour && notAnOddCentury;
The values divisibleByFour and notAnOddCentury would still have been ANDed first, and then ORed with isAFourthCentury. Go through the analysis on your own now. If the year qualifies as a leap year by dividing by four, but (AND) not dividing by 100, it is a leap year; OR, if the year qualifies by dividing evenly by 400, then it is a leap year.
Make sure you understand this process before you go on. It may appear complicated, but it is not; each evaluation is as simple as "yes it is" or "no it is not". Once you get the feeling for the testing process here, you will be able to use the relational operators, the logical operators, and even the modulo operator to evaluate much more complex decision making conditions.
A Handy Tool
Since you have been working on analyzing these Boolean expressions, it might be handy to introduce you to a tool for making the analysis process a little easier. The Truth Table is usually a small chart or table that you create to identify the results of various Boolean expressions.
Consider for a moment the Boolean expression A && B. This expression should be easy to analyze, but to be thorough in your program study, you use a Truth Table to do the analysis. The Truth Table would be implemented as shown below.
For the condition A && B:
Consider the A || B combination, shown next.
Finally, consider the Truth Table for the leap year expression discussed previously:
isLeapYear = divisibleByFour && notAnOddCentury || isAFourthCentury;
From your analysis of the table above, you should be able to work out the evaluation of each of the given conditions. The Truth Table can be your best friend when you are trying to resolve what decisions will be made out of the logical expressions you have put together. It takes a few minutes, but it will keep you from making silly mistakes. Consider this final logical condition example, and short history lesson, to apply your knowledge of the Truth Table.
Augustus de Morgan was a mathematics professor who backtracked the logic of negative logical relationships and how they impacted the AND and the OR logical operations. In a nutshell, his law states that NOT (A OR B) is the same thing as NOT A AND NOT B. The AND/OR relationship is the same: NOT (A AND B) is the same thing as NOT A OR NOT B.
This is an interesting practical application of your Truth Tables. Consider the application of his law in the following Truth Table. Remember as you analyze this that NOT simply turns the thing it is converted to into the opposite thing (i.e., NOT true is false, and NOT false is true).
|A||B||Not(A or B)||Not A and Not B|
Make sure you understand how each of the values were found in this example. de Morgan's Law is fairly simple and can be found at any time by just implementing a truth table. Just make sure you can create and apply a Truth Table, even when the conditions are all negated (i.e., they all use NOT).
To A or NOT to B
This first chapter on Boolean conditions will help you get started in thinking about logical operations, and how to apply them. Whenever you need to apply logical conditions to any problems, you must evaluate all the consequences of a given combination of logical operations. In the next chapters, you will start learning how to use these conditions to change the direction of your programs - in other words, to make decisions.
- there are six basic relational operators that you will be using; they are the "test for equal" ("=="), "test for less than" ("<"), "test for greater than" (">"), test for greater than or equal" (">="), test for less than or equal" ("<="), and "test for not equal" ("!=")
- the data type that holds relational or Boolean data is the bool type; it holds the values true and false
- it is possible to do multiple tests with relational operators, but not recommended; the results may be unexpected
- the logical operators are AND ("and" or "&&"), OR ("or" or "||"), and NOT (not or "!"); generally speaking, these can be used in any combination; however, you should always use the character operators instead of the words unless you are working on a keyboard that does not support the characters
- there is an order of operations with the logical operators, however, just like mathematical expressions, you are much wiser to use parentheses generously to make your logical process intentions clear
- the OR operation requires that one or more of the evaluated items in an expression be true
- the AND operation requires that all of the evaluated items in an expression be true
- the NOT operation inverts or reverses any operation or group of operations in which it is placed in front
- Truth Tables are a powerful tool for evaluating combinations of logical conditions; by going through all the possible combinations, you will not discover any surprises when you apply the logical expression