Skip to content

Latest commit

 

History

History
283 lines (196 loc) · 10.5 KB

booleans.md

File metadata and controls

283 lines (196 loc) · 10.5 KB

What's News

Despite advances in the understanding of quantum mechanics, researchers at the world's leading institutes for science have finally boiled down the essence of matter into one of two states. Read on to find out about their amazing discovery!

Boolean

Booleans are a conceptual thing and a technical thing in C++. Named after their famous discover, George Boole, Booleans (and their C++ type bool) are either true (true) or false (false). Booleans remind me of the phrase Yoda made famous: "Do or do not. There is no try." Booleans are similar: True or False. There is no maybe.

Like all types in C++ we know, bools have two characteristics:

  1. A range of valid values; and
  2. Valid operations on those values.

Like ints which can only hold whole numbers (positive and negative), bools are limited in what they can hold. Valid values for bools are true or false. That's it.

What is cool about George Boole's thinking was that he defined a set of operations that you can use to manipulate Booleans and get another one (we'll come back to those operators in a second). What's even more awesome is that those operators closely reflect how logic works in real life!

So far we have learned that bools can only hold true and false. We know that we have operators to combine two Booleans and get another. But, how do we get a Boolean value in the first place?

The answer: relational and equality operators! An expression composed of operands and a relational or equality operator has a value (just like all expressions!). And, like every value in C++, it has a type! The value can be either true or false and the type is bool. I am sure that you aren't surprised!

Let's look at the relational and equality operators in "C++ World" and how they compare to the ways that we write comparisons when we are in "Math World."

Meaning Math World C++ World
Less than $\lt$ <
Less than or equal to $\leq$ <=
Greater than $\gt$ >
Greater than or equal to $\geq$ >=
Equal to $=$ ==
Not equal to $\ne$ !=

The first four are the relational operators and the final two are the equality operators.

Let's look at a few examples to make it clear:

#include <iostream>

int main() {
  bool result{5 < 10}; 
  return 0;
}

result is true!

#include <iostream>

int main() {
  bool result{10 <= 10};
  return 0;
}

result is true!

#include <iostream>

int main() {
  bool result{10 >= 10};
  return 0;
}

result is true!

int main() {
  bool result{10 == 10};
  return 0;
}

result is true!

int main() {
  bool result{10 != 10};
  return 0;
}

result is false!

Now, there's something really philosophical about bool in C++. Like C++ is able to coerce an int into a double (or vice versa), C++ can coerce values that are not bool into a bool. That's really powerful (as we'll see later!). Although the specifics are sometimes odd, the basic idea is that if you assign a value (from a type such as an int or a double or char) to a bool and that value is 0 (0.0 or '\0'), then the bool is false. For any other value, the bool is true. For instance,

#include <iostream>

int main() {
  int zero{0};
  double zero_point_zero{0.0};
  char null_character{'\0'};
  char letter_a{'a'};
  int five_hundred_one{501};
  double pi{3.14};

  bool zero_bool{zero};
  bool zero_point_zero_bool{zero_point_zero};
  bool null_character_bool{null_character};
  bool letter_a_bool{letter_a};
  bool five_hundred_one_bool{five_hundred_one};
  bool pi_bool{pi};

  return 0;
}

zero_bool, zero_point_zero_bool, and null_character_bool are all false! The others are true.

Now, let's get back to how we can combine Boolean values and get new Boolean values. Like +, -, *, /, etc. are primitive operations for numbers (integers and doubles), there are a few primitive operators for bools: && (which means and), || (which means or) and ! (which means not).

The first two (&& and ||) are binary operators (which mean that they take two operands, like +, and - do). The ! is a unary operator, meaning that it only takes a single operand.

Think about statements like:

It is raining and it is snowing.

and

It is raining or it is snowing.

Assume that we are in Cincinnati in June and I look outside. I see that it is raining, but there's no way that it's snowing. So, yes, it is true that it is raining (we'll call that raining and say it is true) but, no, it is not snowing (we'll call that snowing and say that it is false).

The first statement is logically not true! So, we would say that it is false. The second statement, however, is true. So, in

int main() {
  bool raining{true};
  bool snowing{false};
  bool is_raining_and_snowing{raining && snowing};
  bool is_raining_or_snowing{raining || snowing};
}

is_raining_and_snowing is false and is_raining_or_snowing is true.

In an || expression, if either operand is true, then the value of the expression is true. In an || expression, if both of the operands are false, then the value of the expression is false. In an && expression, if both operands are true, then the expression is true. Otherwise, the expressions are false.

The ! simply reverses the value of its (single) operand.

There is a cool chart that we can write that describes completely the values for combinations of Boolean values using the and, or and not operators. The chart is called a truth table. Here is the truth table for the and operator:

a b a && b
true true true
true false false
false true false
false false false

Notice that the only combination that is true is when both a and b are true. Interesting!

Now, here is the truth table for the or operator:

a b a || b
true true true
true false true
false true true
false false false

Notice that the only combination that is true is when one of a and b are true. Doubly interesting!

The not operator is different than the and and the or operators. The not operator needs only one operand (a so-called unary operator). Why is that? Well, the not operator simply reverses the value of a Boolean. So, it would make sense that it would only work on one operator. Here's the truth table for the not operator:

a ! a
true false
false true

Let's play computer and try to figure out the value of no in the following C++ code:

#include <iostream>

int main() {
  bool yes{true};
  bool no{!yes};
  return 0;
}

no is false. To compute that, just replace yes with its value (bool no{!true}), flip true to false because of the ! and you get bool no{false};. Pretty cool!

So, we can calculate true and false values, but what good are they? Read on to find out!

It Takes bool to Tango: The if Statement

To date, the programs that we have written in class and in lab have been pretty boring. They may have calculated different values based on user input, but they performed the same set of operations no matter what. In other words, until now our programs have operated sequentially. With an if statement we can get our programs to operate selectively. In other words, depending on a runtime calculation, our program can perform different operations! Although conceptually very simple, the power of this selective computation is infinite! The general form of an if statement is

if (boolean expression) {
  some statements;
} 

When boolean expression is true, then some statements execute. When boolean expression is false, some statements do not execute. some statements between {} are called a block. You can nest blocks of code inside one another without limit (some caveats apply to that statement)! C++ programmers say that a block of code is "conditionally executed" by an if statement. C++ programmers also often say that a block of code is "guarded by a conditional".

#include <iostream>

int main() {
  int five = 5;
  int six = 6;

  if (5 < 6) {
    std::cout << "Five is less than 6!\n";
  }

  if (five == six) {
    std::cout << "Huh, five is equal to 6!\n";
  }
  return 0;
}

prints

Five is less than 6!

Because 5 is less than 6, the std::cout statement inside the block associated with the 5 < 6 condition executes and Five is less than six! is printed to the screen. However, the value of five does not equal the value of six so the std::cout inside the block associated with the five == six condition does not execute.

Be very careful: = and == are different!

C++ tries to be very accommodating and will attempt to convert a value of any type to a bool when it is used in the place of boolean expression in an if statement. Remember, from earlier, how we talked about the ways that conversions happen?

More importantly, remember how we talked about the fact that many things in C++ are expressions that you would not immediately realize?

Well, one of those hidden expressions in C++ is the = (assignment) operator! Yes, an assignment statement like a = b is, in fact, an expression and its value is the value of b! In other words,

#include <iostream>

int main() {
  int a{5};
  int b{6};

  std::cout << "a = b: " << (a = b) << "\n";
  return 0;
}

prints

a = b: 6

I know, right?! These semantics make it possible to do things like

a = b = c = d = 5;

to set a, b, c and d all to the value 5.

Now, think closely and figure out why

#include <iostream>

int main() {
  int five{5};
  int six{6};

  if (five = six) {
    std::cout << "Huh, five is equal to 6!\n";
  }

  return 0;
}

prints

Huh, five is equal to 6!

If you look closely, you will see that where I put boolean expression in the if statement there is a = and not a ==. The expression evaluates to the value in six. Remember earlier how we learned that C++ interprets anything that is not a bool and is not another type whose value is something like 0 (etc.) as true? Well, in the case shown above, C++ evaluates whether the value of the expression five = six is 0 (it's not, it's the value in six), determines that the value equals true and chooses to execute the block!

Be very careful! You will make this mistake! I promise!