Binary Logic

The Logical Datatype (bool)

Binary logic is how we decide the actions of a computer based on whether a condition is true or false.

We encounter binary logic all the time in everyday life. If you are out of toilet paper, you buy toilet paper. The action of buying toilet paper is only executed if the condition of being empty is true. The condition can only be true, or false.

When coding, we use boolean variables to hold and represent these true or false values. Boolean, or bool variables can only hold true or false.

bool false_value = false;
bool true_value = true;

Relational operators compare values to get a boolean condition. In the same way 3+3 evaluates as 6, 4<6 evaluates as true, and 4>6 evaluates as false.

To check if a value is equal to another value, we use the double equals operator (==). Be careful not to use 1 equals sign, as that will make the program attempt to assign the right value to the left side.


Here is a list of relational operators and how they evaluate:

Operator Symbol Evaluation
Greater than x > y Evaluates as true if x is greater than y, and false if it is less than or equal to y
Less than x < y Evaluates as true if x is less than y, and false if it is greater than or equal to y
Greater than or equal to x >= y Evaluates as true if x is the same value or greater than y, and false if it is less than y
Less than or equal to x <= y Evaluates as true if x is the same value or less than y, and false if it is greater than y
Equal to x == y Evaluates as true if x and y have the same value, and false if they differ
Not equal to x != y Evaluates as true if x differs from y, and false if they are the same value

If statement

if (condition)
{
	// Code execution...
}

The if statement is the foundation of computer logic. It evaluates a bool condition and executes a block of code if the condition is true. The code execution skips the block of code by jumping to the closing curly brackets if the condition is false.


Let's go over a program that checks if the user needs milk as an example. We start by asking the user to input how much milk they currently have.

    std::cout << "How much milk do you have?\n";
    float milk_amount;
    std::cin >> milk_amount;

Then we use the if statement to check if we have to buy more. In this case, we will buy more milk if the amount of milk we have is equal to 0.

#include <iostream>

int main() 
{
	std::cout << "How much milk do we have?\n";
	float milk_amount;
	std::cin >> milk_amount;

	if (milk_amount == 0) 
	{
		std::cout << “Buy milk!”;
	}
}

Here the condition milk_amount == 0 has to be true to execute the attached block of code.

We still need to give the user feedback if they do have milk.

#include <iostream>

int main() 
{
	std::cout << "How much milk do we have?\n";
	float milk_amount;
	std::cin >> milk_amount;

	if (milk_amount == 0) 
	{
		std::cout << “Buy milk!”;
	}
	if (milk_amount > 0) 
	{
		std::cout << “You don't have to buy milk!”;
	}
}

This program gives the correct feedback for valid inputs, but it breaks if the user inputs a negative number.

To fix this we can add yet another if statement to check if the user inputs a negative number.

if (milk_amount < 0) {
	std::cout << "You can't have negative amounts of milk!";
}
Well done!

Try out this task to learn how to apply the if statement to make a calculator that lets the user select what calculation they want.

Multiple conditions

The logical AND operator (&&) combines 2 conditions and returns true only if both sides are true.

// and_condition will only be true if `condition1` and `condition2`  are true.
bool and_condition = condition1 && condition2;

It is useful when multiple conditions must be true to execute a specific block of code.

The logical OR operator (||) returns true if either or both sides are true, and false only if both sides are false.

// or_condition will be true if either `condition1` , `condition2`, or both are true.
bool or_condition = condition1 || condition2;

It is useful when executing a block of code if at least one of the multiple conditions is true.


This calculator takes 3 values and outputs them all divided by each other like this: in1/(in2/in3)

#include <iostream>

int main() 
{
	float in1, in2, in3;
	std::cin >> in1 >> in2 >> in3;

	std::cout << in1/(in2/in3);
}

Dividing by 0 is dangerous and can cause the program to crash. To avoid this, we need to ensure that in2 and in3 is not 0.

We can use the || operator with the == operator on each side to check if both variables are equal to 0. We can warn the user if either condition is true.

if (in2 == 0 || in3 == 0) 
{
	// This will only execute if either in2 or in3 is 0
	std::cout << "Cannot divide by 0. Make sure your second and third inputs aren't 0.";
}

We can use the && operator to check that both variables are not equal to 0 by using the != operator.

if (in2 != 0 && in3!=0)
{
	// This will only be executed if both in2 and in3 are not equal to 0
	std::cout << "The result of " << in1 << "/(" << in2 << "/" << in3 << ") is " << in1/(in2/in3);
}

These are logical opposites. One of them will always be true and the other false.

condition1 != 0 && condition2 != 0 will only be true when condition1 == 0 || condition2 == 0 is false

Because of this, we have now made the program safe. We warn the user if any of our "dangerous" inputs are 0. We give the user the answer to the calculation if our inputs are safe, i.e. not 0.

Boolean casts and the ! operator

The AND (&&) and OR (||) operators are both logical operators. The last logical operator is the NOT (!) operator. An exclamation mark will flip the boolean value of an expression.

bool var1 = !true; // var1 will hold the value: false
bool var2 = !false; // var2 will hold the value: true

You can use the NOT operator when you have a piece of code you want to execute when a condition isn't met.

if (!(4 > 5))
{
	std::cout << “This piece of text will print”;
}

4 > 5 is evaluated as false and then flipped to true by the NOT operator. The if statements condition is true, and the code will execute.


Implicit bool cast

The ! operator is high up in the order of operations. This means that the computer considers it before other operators.

This is important because it makes statements like !5 < -1 different from !(5 < -1).

This is because !5 is processed before the lesser than operator.

The number 5 is implicitly cast to a boolean because you cannot use the ! operator on an int.

A number cast to a boolean will generally be true if it is a number above 0, and false if it is 0 or lower.

Once 5 has been cast to true, ! flips its value to false . Now you have false < -1.

The value false is cast to an int because you cannot use the < operator on a bool.

Booleans cast to numbers will almost always result in it being 1 if it is true, and 0 if it is false. In this case, !5 / false is 0 when cast to an integer.

The final expression is 0 < -1, which is false. !(5 < -1) doesn't do any casting, and is true.



Logical operators

Operator Example Evaluation
` `
&& x && y Evaluates as true if both x and y are true. If either is false it evaluates as false.
! !x Evaluates as true if x is false, and false if x is true.

else

The else statement is a following statement for an if statement. It executes a block of code when the if-statements condition is false.

if (condition)
{
      // This code runs if condition is true
}
else
{  
      // This code runs if condition is false
}

You can only use it after an if-statement.


In a previous example, we used 2 if statements to make the program do 1 of 2 things.

if (in2==0 || in3==0)
{
	std::cout << "Cannot divide by 0, make sure your second and third input isn't 0.";
}

if (in2 != 0 && in3!=0)
{
	std::cout << "The result of " << in1 << "/(" << in2 << "/" << in3 << ") is " << in1/(in2/in3);
}

One of these if statements will be true and the other will be false. In cases like this, we can use the else statement to improve the readability and performance of our code.

if (in2 == 0 || in3 == 0)
{
	// This will be executed if in2 or in3 is 0
	std::cout << "Cannot divide by 0, make sure your second and third input isn't 0.";
}
else
{ 
	// This will be executed if the code above isn't executed (i.e if in2 and in3 are not 0)
	std::cout << "The result of " << in1 << "/(" << in2 << "/" << in3 << ") is " << in1/(in2/in3);
}

The else-statement clearly shows that only one block of code will execute.

This also improves performance because the computer has to do fewer comparisons. The performance benefit is unnoticeable in an example like this. But this can save a lot of processing power when the condition is a resource-intensive calculation.

else if

Code after an else-statement will execute if the connected if-statement is false.

We can chain an else if to an if-statement to get an if-statement that evaluates when the condition of the first if-statement is false.

#include <iostream>

int main()
{
	std::cout << "input the operation you want to perform\n1=addition\n2=subtraction\n3=division\n4=multiplication";

	int operation;
	std::cin >> operation;

	std::cout << "input the 2 numbers you want to use (separated by space)";
	int in1, in2;
	std::cin >> in1 >> in2;

	if (operation == 1)
	{
		std::cout << in1 << " + " << in2 << " = " << in1+in2;
	}
	else if (operation == 2)
	{
		std::cout << in1 << " - " << in2 << " = " << in1-in2;
	}
	else if (operation == 3)
	{
		std::cout << in1 << " / " << in2 << " = " << in1/in2;
	}
	else if (operation == 4)
	{
		std::cout << in1 << " * " << in2 << " = " << in1*in2;
	}
	else
	{
		std::cout << "Invalid operation type input";
	}
}

The code execution jumps to the end of the chain as soon as any statement evaluates as true and the attached block of code has been executed.

Chaining together conditions and code makes it clear that we only want one of the pieces of code to execute. It also increases performance because the rest of the else if statements are ignored if a previous if or else if condition is true.


You can use the order of else if statements to avoid having to make the same comparisons again.

Because the code execution goes from top to bottom, you can put "blanket conditions" early in the else if chains.

The condition of earlier if/else-if statements have to be false for code execution to get to an else if statement. This means that you don't have to make the same comparison again, because you already know that previous conditions were false.

In the previous section, we used this piece of code to make sure that in2 and in3 were not 0.

if (in2 == 0 || in3 == 0)
{
	std::cout << "Cannot divide by 0, make sure your second and third input isn't 0.";
}
else
{ 
	std::cout << "The result of " << in1 << "/(" << in2 << "/" << in3 << ") is " << in1/(in2/in3);
}

We could chain else if statements together like below to tell the user exactly which input was wrong.

if (in2 == 0 && in3 == 0) 
{
	std::cout << "Both inputs are invalid";
}
// If the code execution reaches this else if statement we know that max one input is 0, so we don't have to check if in3 is 0
else if (in2 == 0)
{
	std::cout << "Your second input is invalid";
}
// This code is only executed if in2 is not 0, so no point in checking.
else  if (in3 == 0)
{
	std::cout << "Your third input is invalid";
}
// The else statement is only reached if neither in2 nor in3 are 0.
else
{
	// Inputs are valid. Give the answer
	std::cout << "The result of " << in1 << "/(" << in2 << "/" << in3 << ") is " << in1/(in2/in3);
}

If if (in2==0) was the first if-statement, there is still a possibility that in3 is 0. We would have to check for that after confirming that in2==0. Since if in1==0 && in2==0 is the first condition, we know that in2 cannot be 0 if in3 is 0 by the first else if statement.

Scopes - If statement blocks

The blocks of code indicated by the curly brackets are called scopes. In addition to grouping code, scopes serve as a container for variables and other things you declare.

Inner scopes have access to the variables and symbols of its enclosing outer scopes. Outer scopes cannot access the variables and symbols of inner scopes.

#include <iostream>

// global scope
int x = 10;

int main() 
{
	// y is in the main function's scope 
	int y = 20; 

	// This block defines a new, inner scope
	{
		// z is in the inner scope
		int z = 30; 
		// Okay, all variables are visible in the inner scope
		std::cout << x << " " << y << " " << z << "\n";
	}
	// error, z is not visible in the main function's scope
	std::cout << x << " " << y << " " << z << "\n"; 
}

This code will not compile because the variable z isn't found in the scope.


The compiler gets mad at you if you try to define a variable with the same name as another because it won't know which variable to use when you use its name.

However, you can declare a variable of the same name as a variable in an outer scope. The compiler will use the variable from the innermost scope when it encounters the variable name. You should still avoid using the same variable name. But now you know what the behavior looks like in case you accidentally name your variables the same.

#include <iostream>

int main() 
{
	int example = 4;

	if (true)
	{
		int example = 7;
		std::cout << example; 
		example = 3;  
	}
	std::cout << example;
}
74

Not all scopes are explicitly defined through the curly brackets.

The curly brackets of if, else if, and else statements aren't required. If there are no curly brackets; the compiler treats the following statement as the "block of code".

if (4<5)
	std::cout << "1";
std::cout << "2";
if (4>5)
	std::cout << "3";
std::cout << "4";

This program outputs 124. note that only the 3 is missing because it's the only statement tied to an if statement with a false condition.

Despite the lack of curly brackets, this too is a scope. Any variables you declare inside the if statement are unavailable outside of it.

If you remove the code associated with an if statement without removing the if statement it might attach to the code below it. This can cause unintended behavior.

Ternary operator

condition ? expression1 : expression2

This is a ternary operator. You use it like an if statement followed by an else statement. However, instead of having 1 of 2 scopes executed, it consists of 2 expressions it can execute depending on the condition.

expression1 is used if the condition is true. expression2 is used if the condition is false.

This is often used to define a variable as different things depending on some condition. For example, if we want to get the absolute number of a variable, we want to make sure we flip the value if it is negative.

This is how it looks with if statements

int var;
std::cin >> var;

int absolute_var = var;

if (absolute_var < 0)
	absolute_var = -absolute_var;

This is how it looks with the ternary operator

int var;
std::cin >> var;

int absolute_var = var < 0 ? -var : var;

Switch statements

#include <iostream>

int main()
{
	std::cout << "input the operation you want to perform\n1=addition\n2=subtraction\n3=division\n4=multiplication";

	int operation;
	std::cin >> operation;

	std::cout << "input the 2 numbers you want to use (separated by space)";
	int in1, in2;
	std::cin >> in1 >> in2;

	if (operation == 1)
	{
		std::cout << in1 << " + " << in2 << " = " << in1+in2;
	}
	else if (operation == 2)
	{
		std::cout << in1 << " - " << in2 << " = " << in1-in2;
	}
	else if (operation == 3)
	{
		std::cout << in1 << " / " << in2 << " = " << in1/in2;
	}
	else if (operation == 4)
	{
		std::cout << in1 << " * " << in2 << " = " << in1*in2;
	}
	else
	{
		std::cout << "Invalid operation type input";
	}
}

If-else chains where the same integer is being compared multiple times for equality can become quite lengthy and convoluted.

We can use a switch statement to create more concise and readable code. This eliminates the need to make sure it's the same variable in every statement when reading the code.

This is the same code as a switch statement.

#include <iostream>

int main()
{
	std::cout << "input the operation you want to perform\n1=addition\n2=subtraction\n3=division\n4=multiplication";

	int operation;
	std::cin >> operation;

	std::cout << "input the 2 numbers you want to use (separated by space)";
	int in1, in2;
	std::cin >> in1 >> in2;

	switch (operation)
	{
		case 1:
		std::cout << in1 << " + " << in2 << " = " << in1+in2;
		break;
		case 2:
		std::cout << in1 << " - " << in2 << " = " << in1-in2;
		break;
		case 3:
		std::cout << in1 << " / " << in2 << " = " << in1/in2;
		break;
		case 4:
		std::cout << in1 << " * " << in2 << " = " << in1*in2;
		break;
		default:
		std::cout << "Invalid operation type input";
		break;
	}
}

The expression inside the switch (...) statement is an integer. The code execution will jump to the case that matches the integer expression.

The break statement exits the switch statement. This jumps code execution to the end of the attached scope.

Without break; the code execution will "fall through". This means that it continues in subsequent cases until it reaches a break; statement.

This often leads to unintentional behavior, but you can utilize it when you want multiple cases to execute the same block of code.

If none of the cases match the expression, code execution will simply continue after the switch statement.

If we want a piece of code to execute when none of the cases are found, like an else statement, we can use a default: case.

If no matching cases are found inside the switch statement, the code execution will continue at the declared default case, if it is declared.

Questions

Questions

Questions

Questions

Questions

Questions

Questions

Questions

Questions

Questions