int n = 4.6 * 2;
double x = 7 / 2;
cout << "n: " << n << ", x: " << x << endl;
what are logical operations?
what is the precendence of C++ operations?
what are type conversions?
A logical expression evalutes to a Boolean value: true
or false
.
We use logical expressions in conditions for flow control (e.g.,
if
statements and while
loops).
So far, our logical expressions have consisted of comparison operators:
Operator  Operation 


Less than? 

Greater than? 

Less than or equal to? 

Greater than or equal to? 

Equal to? 

Not equal to? 
That’s all we’ve needed thus far in the course, but now we will start to write
more complicated logical expressions.
These will involve logical operations.
Logical operations, just like arithmetic and comparison operations,
operate on values and evaluate to a result.
Some operate on two values (just like x + y
): these are called
binary operations.
Others operate on a single value (just like x
): these are called
unary operations.
Logical operatons, like comparison operations, always evaluate to
true
or false
.
Unlike arithemetic or comparison operations, however,
logical operations also operate on Boolean values.
We will concern ourselves with four logical operations in this course:
Keyword  Operator  Kind  Evaluates to true iff: 



Binary 
both operands are true 


Binary 
at least one operand is true 


Binary 
exactly one operand is true 


Unary 
the operand is false 
These operations mostly correspond that what you’d expect from colloquial use (in everyday speech). However, we can define them a bit more rigorously using a truth table. As an exercise, fill in the following truth table. Use the definitions of the various logical operators above.
a 
b 
a and b 
a or b 
a xor b 
not a 

F 
F 

F 
T 

T 
F 

T 
T 
These operations will allow us to construct more interesting conditions like
"$n$ is between 3 and 7".
Mathematically, we would write that expression as $3 \leq n \leq 7$,
but 3 ⇐ n ⇐ 7
does not have the same meaning in C++.
Let’s explore why below.
We have previously used an informal notion of precedence when evaluating expressions: "multiplication and division occur before addition and subtraction". Now, when we add the logical operations into the mix of the kinds of things we can put in our expressions, things get a little bit more complicated. Here is a table that shows the precedence of all of the operations we now know about:
Operator  Description 


subexpression 

logical not, unary positive, unary negative 

multiplication, division, modulus 

addition, subtraction 

relational inequalities 

equal / not equal 

logical and 

logical or 

assignment 
It is very common, in a variety of algorithms, to need to update a variable
inplace by applying an arithmetic operation.
For example, our
printNumbersUpTo
example
included the statement i = i + 1
, and our
factorial
example included
number = number  1
.
In C++ (and many other languages!), we can write these sorts of
"apply an arithmetic operation and then save the result in the same variable"
using compound operations.
Here are a table of such operations:
Operation  Equivalent to 









So, instead of writing x = x * 2
, we can write x = 2
.
Instead of i = i + 1
, we can write i += 1
.
However, in fact, the need to *increment (add one) and decrement (subtract
one) is so common that they have their own operators.
The ++
operator means, "add one to a variable and store the incremented value
back in the variable" and 
means the same for subtraction.
Operation  Equivalent to  Also equivalent to 







The ++
and 
operators can be applied either before or after
a variable (e.g., i++
or ++i
).
In both cases, the value will be incremented by one.
The only difference between them is what the whole expression will evaluate to:
the old value of the variable or the new value.
This distinction will not matter in code I show you in ENGI 1020.
In fact, code that relies on the distinction between i++
and ++i
may be
a bit too clever for its own good: this kind of tricky code is also
tricky to understand and tricky to debug, so it’s best to avoid writing it.
What does the following code output?
int n = 4.6 * 2;
double x = 7 / 2;
cout << "n: " << n << ", x: " << x << endl;
In the first case, we are multiplying a floatingpoint number by an integer, then storing the result in an integer variable. The computer cannot do this directly. In the second case, we are dividing an integer by an integer and then storing the result in a floatingpoint variable. The result of this initialization might surprise you.
When we perform an operation on integer and floatingpoint numbers, we must
convert one to the other.
In C++ (and many, but not all programming languages), the compiler will
automatically convert an integer to a floatingpoint number when it’s involved
in a floatingpoint operation (addition, subtraction, etc.).
This numeric promotion is safe in the sense that
no information is lost: it’s safe to refer to 3
as 3.0
, etc.
The same kind of promotion occurs when we store integer values in
floatingpoint variables (including parameters!).
In the other direction, we can also assign floatingpoint values to integer
variable (including parameters).
When this happens in many — but not all — programming languages,
the floatingpoint value is truncated: anything after the decimal place
is simply chopped off.
Even though 4.9
is almost 5
, it will be truncated to 4
if stored in an
integer variable.
In most programming languages, numbers can be converted to Boolean values and viceversa. Boolean values are converted to integer types as follows:
true
becomes 1 and
false
becomes 0.
Integer types include int
, long int
and even char
(which, after all, is
an integer code for a character).
The above conversion might be about what you expect, but you may find the
reverse to be slightly surprising:
0 becomes false
and
anything else becomes true
.
These conversions are often applied automatically. For example, the following code will compile and run just fine:
int i = getNumberFromUser();
if (i)
{
cout << "i is not zero!\n";
}
When evaluating a boolean condition, we follow the order of operations to reduce
the condition down to a single true
or false
value.
However, sometimes we don’t need to evaluate the whole condition to know the
answer!
Consider the following code:
bool sleptWell = false;
bool haveCoffee = true;
if (haveCoffee or sleptWell)
{
giveLecture();
}
This condition will be true
if either haveCoffee
or sleptWell
are
true.
Consequently, as soon as we see that haveCoffee
is true, there is no need to
continue checking the rest of the condition (sleptWell
): we already know what
the answer is going to be.
This is the shortcircuit property: as soon as we know what a condition will
evaluate to, we can stop evaluating it.
This may not seem terribly exciting when checking boolean variables, but
consider a different case:
bool foo(double x) { return (x > 0); }
bool bar(double x) { cout << x; return (x > 1); }
int main()
{
if (foo(2) or bar(2))
{
cout << "wibble";
}
return 0;
}
In this case, the behaviour of the program is changed by the shortcircuit
property: the bar
function will never run.
This is worth understanding when we write conditions for if
statements
and loops!
Given four integer variables, A, B, C and D,
write four C++ functions functions to return true
iff:
any of them is negative,
A is the smallest value,
any pair is equal and
the product of any pair is equal to the product of the other pair.
By default, the C++ compiler will truncate floatingpoint numbers when storing them in integers, i.e., chop off anything after the decimal point. The standard library does include several rounding functions, but let’s write one for practice. Write a function that rounds a floatingpoint number to the nearest integer. It would be advisable to design before implementing: create the contract of the function and think about all of its logic, the special cases it needs to handle, how to test it, etc., before you write any code.
(c) 2009â€“2018 Michael BruceLockhart, Theo Norvell, Dennis Peters and Jonathan Anderson. Licensed under a Creative Commons Attributionâ€“Noncommercialâ€“ShareAlike 2.5 Canada License. Permissions beyond the scope of this license may be available at theteachingmachine.org.