Relational operators
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.
Operator |
Definition |
Meaning |
== |
equality |
if the thing on the left side has a value equal to the thing on the right side, evaluate to true; otherwise, evaluate 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; otherwise 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; otherwise 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; otherwise 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; otherwise 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; otherwise 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 (with one little caveat discussed - see "Floating point values aren't precise" below). 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 );
Officially speaking, the relational operators have equal weight. And generally speaking, expressions are evaluated left to right. However, ANDed items must work together while ORed items aren't as critical; this kind of gives the AND operation an effective precedence over OR. That said, with or without precedence, if you analyze the logic for any given expression, you will find that there won't be another way to evaluate the expression anyway.
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 has no value
// it will not return a correct result
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 resolve to or "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 resolves to, or 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.
Little Side Issue: Floating point values aren't precise
As has been discussed in an earlier chapter in this reference, integer values are stored exactly as they are entered. However, unlike integers that are simply translated to binary and stored as is, floating point values such as doubles are stored as exponential quantities (e.g., 4.356 x 25). This allows floating point values to represent numbers well beyond the limits of integers. That is the good news. The bad news is that the mantissa or significand (i.e., the decimal number in the exponential expression) only holds a limited number of digits. Thus, even if the number might be represented in 20 digits (e.g., 5.678915973564281673), it would likely be stored as ~5.678915973 x 260 in a 32-bit system, which of course would lose all the lower-level digits; this constitutes a loss of precision.
But it gets worse. Since floating point values are imprecise, you will find that a number that displays as 5 is very likely to be stored as 4.999994635 or 5.000018362. And it gets worse than that. If you use the methods pow or exp, or virtually any of the Math class methods that manage large numbers, they tend to use logarithms. And while logarithms do a pretty good job, they are still just close estimations. So why does this matter for decision making? The problem is since you can never be sure that some variable a is really holding exactly 5, and some other variable b is really holding exactly 5, you cannot compare them for equality. Generally speaking, you can compare them for less than or greater than as long as the difference is expected to be larger than one or two. However, and to repeat, you must never compare floating point values for equality. Your results will be inconsistent and almost always incorrect.
There are some tricks such as bracketing that can help with this. For example, if you want to test some variable varOne to see if it is zero, you can check for varOne > -0.0001 && varOne < 0.0001. The number of digits of precision or accuracy is up to the needs of the program you are writing, and you will have to understand the appropriate limit for that program. However, even this system can run into flaws. Your best bet is to try to stay away from testing floating point values against exact values. You will find it necessary at times, so you will need to demonstrate your best problem-solving skills at that point, but otherwise try to stay away from this kind of test if it is possible.
Comparing values
This brief topic provides the syntax and the fundamental operations necessary for program comparison, and later on, branching or selection. It is mostly an introduction and a reference for the testing tools you will be using in your programs. However, be prepared to come back to this topic when you need to verify that you are using the correct operator and/or you need to see how the testing actions work, or sometimes don't work as you noted with floating point values. This foundation will support the next steps you will take as you learn about program decision making.