The computer, in its own binary way, sees all things as either true or false. No maybe's; no shades of grey.
The 'inventor' of the way we algebraically manipulate truth values (true and false), was a British mathemetician named George Boole. The algebra he developed is called Boolean algebra. But, in the inimitable style of the C++ language, this nomer has been shortened to bool for our logical data type. However, we did keep the truth values themselves complete: true and false.
Here are a couple of declarations involving bool:
bool done = false, print_symbol_in_front; const bool special = true;
COMING SOON!
typecast --> bool; all-0 bit-pattern --> false; any non-0 bit --> true
typecast bool --> numeric; false --> 0; true --> 1
COMING SOON!
&&, ¦¦, !
To have bool values automatically generated by your program (so you don't have to type in 'true' and 'false' a lot), we use the relational operators from math -- slightly altered:
Meaning | Math | C++ |
---|---|---|
equal to | = | == |
less than | < | < |
less than or equal to | ≤ | <= |
greater than | > | > |
greater than or equal to | ≥ | >= |
not equal to | ≠ | != |
When used to compare two literal values, two variables, two constants, or any pairing of (expressions involving) these, the result is a bool (i.e. one of true or false).
Comparison Evaluates to ----------------------------------- 4 < 5 true 5 < 4 false 6 == 2*3 true 4 != 5 true
Again, any of those literals could be replaced with a variable or a constant (or a function call result or...).
New programmers often get confused when moving mathematical relational operators into C++. The first reason is that there is a new data type: char. How do values of type char compare? They relate to each other based on their ASCII values. So, looking at your ASCII appendix, you'll find that all of the uppercase letters are in order, all the digits are in order, all the lowercase letters are in order. So:
Comparison Evaluates to ----------------------------------- 'A' < 'B' true 'r' > 'm' true '6' > '8' false
But, the kicker is that the uppercase and lowercase letters are separated -- and the uppercase ones come first!
Comparison Evaluates to ----------------------------------- 'A' < 'a' true 'B' > 'b' false
And, confusing to some, all characters are comparable to each other (digits to punctuation to letters to spacing, etc.):
Comparison Evaluates to ----------------------------------- 'A' < '2' false 'b' > '9' true ' ' < ',' true '@' > 'G' false '/' < 's' true
But, the main point of this is that you CAN compare any two character values (or variables or constants or function results or expressions).
There are two basic problems that can arise with equality and non-equality tests: typographical and correctness. The correctness problem only occurs with floating point types. The typographical problems can arise with any other type.
One of the most common errors with comparison is to leave off the second equal sign on an equality test. Of course, this leaves us with an assignment instead of a test. If your compiler has been configured well, it should warn you when you do something like this. But, not all compilers even offer such configuration options!
How can we fix this? Well, if our test involves literals, constants, or expressions, we can place those on the left side of the test. That way, if we accidentally leave off the second equal sign, we'll get an error saying that we cannot assign to a literal, constant, or expression! This will work on ALL compilers.
'Test' Code | Effect | Compiler |
---|---|---|
var = 9 | var is assigned to be 9, bool result is considered true | may warn if properly configured |
var = 0 | var is assigned to be 0, bool result is considered false | may warn if properly configured |
9 = var | illegal assignment | gives an error |
0 = var | illegal assignment | gives an error |
Another typographical error that comes up with comparison sometimes is the reversal of the ! and the = in a non-equality test: =!. This has a complicated effect of automatically typecasting (aka coercion) the right side of the assignment to a bool (0 becomes false and anything else becomes true), logically negating that, then coerces that bool into the type for the left side (false becomes 0 or '\0' and true becomes 1 or '\1'), and finally assigning this result to the left side variable. Whew! Since the compiler reads =! as 'assignment from logical negation of', we can fix this error in the same way we did above: make sure any literal, constant, or expression is on the left of the 'test':
'Test' Code | Effect | Compiler |
---|---|---|
var =! 9 | 9 becomes true, not true is false, false is 0, var is assigned 0, bool result is considered false | may warn if properly configured |
var =! 0 | 0 becomes false, not false is true, true is 1, var is assigned 1, bool result is considered true | may warn if properly configured |
9 =! var | illegal assignment | gives an error |
0 =! var | illegal assignment | gives an error |
Of course, if both sides of these tests are simple variables, we cannot get away from it without compiler configuration help. But it is a good idea to always place literals, constants, or expressions on the left of equality and/or non-equality tests in your programs!
Recalling that only zero, the special 'error' values, and exact powers of 2 are stored exactly for floating point values (2.0, 4.0, 1.0, 0.5, etc.), you may still have trouble deciphering that floating point comparisons of equality and inequality are just as inexact:
Comparison Evaluates to ------------------------------------------------------ 2.0 == 2.0 true 3.0 == 3.0 unknown -- depends on hardware (may not even be consistent!)
The same goes for != comparisons. Typically, when comparing two floating-point values, we use only <, <=, >, or >= because of these round-off issues.
But you can't avoid [non-]equality forever! Sometimes you just have to test for it... So, to test for equality, we can do something like:
fabs(3.0-3.0) <= 1e-6
Here we merely test if the two values are within a certain tolerance of each other. If they are, we call 'em equal. If not, then they are not equal:
fabs(3.0-3.0) > 1e-6
A tolerance is often clear from the context of your program. If not, 10-6 (as above) is a good choice for most situations.
Often a logical condition will depend on not a single comparison, but two or more. For instance, take the simple case of the mathematical notation:
4 <= x < 10
Here we are actually stating two things: x is greater than or equal to 4 and x is less than 10. Both of these things have to be true for the whole expression to hold true. If either is false, the whole test should be false. In Boolean algebra, this is represented by the AND operation. In C++, we denote it with the && operator.
4 <= x && x < 10
This also brings up another point: the mathematical notation would not evaluate correctly without the AND operation. C++ would interpret it this way:
4 <= x < 10 (4 <= x) < 10 // compiler takes this order of operations true/false < 10 // first comparison will be either true or false true
Note that, no matter what value x has, the overall expression is true. This is because in its desperate attempt to compare true or false to 10, it will convert them to an integer value (1 or 0 respectively) and compare that value to 10.
With the AND notation expansion, however, things work out better:
// assume x is 11 4 <= x && x < 10 true && false false
In general, we have the truth table form for AND:
A | B | A && B |
---|---|---|
true | true | true |
true | false | false |
false | true | false |
false | false | false |
Noting that whenever a single one of the logical operands is false, the whole expression is false, the compiler will perform what is known as short-circuit evaluation of the expression. Whenever the left operand is false, it will stop and simply say false -- not even looking at the right operand:
// assume x is 3 4 <= x && x < 10 false // x < 10 isn't evaluated // because left-hand was already // false
More to come...
¦¦ -- inclusive or (written equivalent "and/or")
! -- not
!( < ) ≠ >!!!!
Operator | Logically Opposite Test -----------+-------------------------- == | != < | >= > | <= >= | < <= | > != | ==
var...!var
DeMorgan's Laws
!(A && B) ≡ !A ¦¦ !B !(A ¦¦ B) ≡ !A && !B
exclusive or