Learning Objectives
By the end of today's class, you should know...
  • How do you format decimal numbers for printing using fixed and setprecision(n)
  • What is the library that you must include to use fixed and setprecision(n)
  • What are the 3 logical operators?
  • What is a constant and how do you declare one?
  • What is a magic number and why should they be avoided?
  • What are the short cut assignment operators for add, subtract, multiply, divide, and modulus?
  • What are the increment and decrement operators?
    • What is the difference between pre and post increment and decrement?
  • What is casting?
    • How do you cast an integer to a double and a double to an integer?
  • What happens when an integer is too big for its type?
  • What are the 3 logical operators?
  • How do you test multiple conditions using logical operators?
  • How do && and || operate when:
    • both test conditions evaluate to true?
    • both test conditions evaluate to false?
    • one test condition evaluates to true and one to false?
  • What are some pitfalls you might encounter when using logical operators and how can you avoid them?


Announcements

  • Midterms returned next class
  • Quiz 4 one week from today

Numbers, Formatting and More About Operators

Decimal Formatting

  • Sometimes programs may not display numbers as you would expect!
  • Consider the following program and what it will display:
    #include<iostream>
    using namespace std;
    
    int main() {
        double price = 78.50;
        cout << "The price is $" << price << endl;
    }
    
  • We must explicitly tell C++ how to output numbers in our programs!
  • These commands do not produce any output but change how cout outputs floating-point numbers
  • "Magic Formula" to force decimal places:
    cout << fixed             // fixed notation, not scientific
         << setprecision(2);  // show 2 decimal places
    
  • The commands fixed and setprecision are known as manipulators because you can manually change how cout works
  • You can put both commands on one line:
    cout << fixed << setprecision(2);
  • Also, you can combine the commands with other output:
    cout << "The price is $"
         << fixed << setprecision(2)
         << price << endl;
    
  • To use these commands, you must include the iomanip library:
    #include <iomanip>
    
  • Once we set the decimal formatting, it stays set.


Constants and Magic Numbers

  • A constant variable (or constant) is a variable that cannot change after being assigned a value
  • Sounds oxymoronic, but is actually quite useful
  • To declare a constant, we use the keyword: const
    const int MY_CONST = 1;
  • Note that we must assign a value when the constant is declared
  • Also note that the name is all uppercase letters with an underscore separator
  • This is a common coding convention that you must follow


Magic Numbers

  • Imagine that you are a programmer hired to modify a payroll program
  • You come across the following section of code:
    double pay;
    pay = hours * 7.5 + (hours / 40)
          * (hours - 40) * 7.5 * 0.5;
    
  • The numbers are important to the program, but what do they mean?
  • Numbers like these are called Magic Numbers.
  • They are magic because the value or presence is unexplainable without more knowledge
    • Often, no one knows what they mean after 3 months, including the author
  • A programmer can often infer the meaning of numbers after reading the code carefully
  • A much better coding style is to use named constants rather than literal numbers
  • For example:
    const double WAGE = 7.5;
    const double OVERTIME_ADDER = 0.5;
    const int HOURS_PER_WEEK = 40;
    double pay;
    pay = hours * WAGE + (hours / HOURS_PER_WEEK)
          * (hours - HOURS_PER_WEEK) * WAGE * OVERTIME_ADDER;
    
  • Now it is much easier to understand the code and see any problems or limitations in it
  • Another reason to use named constants is that it is easier to change the value of the number
  • In the above example, we can easily change the WAGE without making errors in other parts of our code


Programming Style: Constant Variables and Magic Numbers

  • Since the meaning of literal (magic) numbers is hard to remember, we should declare constants instead:
    const int FEET_PER_YARD = 3;
    const double PI = 3.14159265358979323846;
    const double WAGE = 7.5;
    const double OVERTIME_ADDER = 0.5;
    const int HOURS_PER_WEEK = 40;
    
  • Note that the name is all uppercase letters with an underscore word-separator
  • This is a common coding convention that you must follow for your constants

More Information


Assignment Operators

  • As we discussed before, we assign values to variables using an equal (=) sign
    int sum = 0;
  • However, the equal sign is really an assignment operator and does not denote equality
  • Thus, unlike math, we can have the same variable on both sides of an equals sign:
    int sum = 25;    // initialize sum to 25
    sum = sum + 10;  // add to sum
    
  • Note that the value of the variable is changed in the second line
  • Reading variables from memory does not change them
  • Values placed into a variable replace (overwrite) previous values:


Shortcut Assignment Operators

  • We can use additional operators to calculate values and assign them to the variable on the left all in one statement
    • Known as shortcut assignment operators
  • The general syntax is:
    variable op= expression;
  • Where op is one of the five arithmetic operators: +-*/%
  • For example, the following two statements create the same result:
    x = x + 3;
    x += 3;
    
  • Shown below are the assignment operators with examples of how they are used:


Summary of Assignment Operators

OperatorDescriptionExampleEquivalent To
=Assigns the value of the expression on the right to the variable on the leftx = 3 
+=Adds the expression on the right to the variable on the leftx += 3x = x + 3
-=Subtracts the expression on the right from the variable on the leftx -= 3x = x - 3
*=Multiplies the expression on the right to the variable on the left and saves the result in the variable on the leftx *= 3x = x * 3
/=Divides the variable on the left by the expression on the right and saves the result in the variable on the leftx /= 3x = x / 3
%=Calculates the remainder from dividing variable on the left by the expression on the right and saves the result in the variable on the leftx %= 3x = x % 3


Increment and Decrement Operators

  • Adding or subtracting one is a common operation in programming
  • C++ provides arithmetic shortcuts for these operations with the increment and decrement operators
  • The increment operator (++) adds 1 to a variable's value
  • Pre-increment adds 1 before evaluating an expression
    ++sum;
  • Post-increment evaluates the expression and then adds 1
    sum++;
  • The decrement operator works like the increments operator, except it subtracts 1 from the variable:
    --sum
    sum--
    
  • Pre- and post- increment matters when the operation is part of a larger expression
  • For example, consider the code:
    int x = 5;
    int y = x++;
    cout << "x=" << x << " y=" << y;
    
  • We may expect y to be 6 after this code executes
  • Instead, y has the value of 5
  • The reason is that ++ after a variable (post-increment) is equivalent to:
    y = x;
    x = x + 1;
    
  • On the other hand, ++ before a variable (pre-increment) is equivalent to:
    x = x + 1;
    y = x;
    

Casting

Cast: change the data type of the returned value of an expression

  • Recall that different data types are stored in different forms
  • Sometimes you need to change from one form to another
  • For example: arithmetic adding a double and an int value
  • C++ will automatically cast one value to another
    • Known as implicit casting or type coercion
  • Programmers can also explicitly cast data types
  • Explicit casting changes the data type for a single use of the variable
  • Precede the variable name with the new data type in parentheses:
    (dataType) variableName
    
  • The type is changed only for the single use of the value
  • For example:
    double x = 2.99999;
    x = (int) x;
    cout << x << endl;
    
  • The value of x is converted from type double to int before assigning the converted value to x
  • However, x remains a type double and the cast only applies to a single use of x
  • The above example shows a common use of casting -- removing the decimal part of a floating-point number
  • Note that the decimal portion of the number is truncated and NOT rounded
  • Decimal part is lost (discarded, ignored, thrown away)
  • Another use is to convert an int to a double when dividing two int numbers and a decimal result is desired
  • For example:
    double x = (double) 9 / 5;
    cout << x << endl;
    
  • Still another use is to prevent compiler warnings
  • For example:
    double x = 2.3;
    int n = x;
    cout << n << endl;
    
  • The above may cause a compiler warning with the settings we use:
    warning: converting to 'int' from 'double'
  • To remove the warning, we use a cast:
    double x = 2.3;
    int n = (int) x;
    cout << n << endl;
    
  • This tells the compiler that you intended to convert from double to int

Example Application Using Casting:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using namespace std;

int main() {
    double input;
    cout << "Enter hours: ";
    cin >> input;

    int hours = (int) input; // prevents warning
    int minutes = (int) ((input - (int) input) * 60);
    cout << "In hours and minutes, this is "
         << hours << ":" << minutes << endl;
    return 0;
}

Integer Overflow

  • An integer is stored in a computer as a pure binary number:

A true binary value.

Image source: Dan Gookin
  • The sign bit sets whether the number is positive (0) or negative (1)
  • The other bits represent the value, in this case 123
  • There are only a finite set of numbers in an integer value

  • What happens when an integer is too big for its type?
    int bigPlus = 2147483647;
    cout << "Big number: ";
    cout << bigPlus + 1 << endl;
    int bigMinus = -2147483647;
    cout << "Small number: ";
    cout << bigMinus - 2 << endl;
    
  • The number "wraps around" from the highest number to the lowest
  • You must be careful that your program will not go beyond the range of its data types
  • Can't sleep: from xkcd


More Integer Types

  • To increase the range, C++ has the long data type
  • Originally, the long data type was 32 bits while the int was 16 bits
  • However, with the development of 32 bit computers, the int value was extended to 32 bits but the long was left at 32 bits
  • Thus, at the present time, int and long are the same size on most computers
  • In addition, C++ has unsigned integer types you can use to change the range
  • Rather than integer ranges from -2147483647 to 2147483647, unsigned int ranges from 0 to 4294967295
  • New to C++11 (a newer version of C++) is the type long long which is a 64 bit type.


Floating-Point Precision and Range

  • Floating-point numbers are not exact representations of real numbers
  • Rounding errors occur in repeated calculations
  • Type double has about twice the precision of type float
  • However, even type double can have rounding errors
    cout << setprecision(17);
    cout << .8F + .1F << endl;
    cout << .8 + .1 << endl;
    
  • When floating point numbers get too large, they are set to inf
  • For instance:
    cout << 2E38F + 2E38F << endl;
  • Similarly, when numbers are too small they are set to 0.0


The Moral

  • Integer and floating-point data types work well most of the time
  • However, if we work with large positive or negative integers, we must be sure we do not exceed the range of the data type
  • Also, floating-point numbers have limited precision
  • When math operations are performed repeatedly, they can become less precise
  • Thus we must be careful of precision when using floating-point numbers


Activity 10.1: Prices (10pts)

  • Find a partner for pair programming.
  • Open up CodeBlocks and copy the following program into your text editor. Save it as price.cpp.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    #include <iostream>
    #include <iomanip>
    using namespace std;
    
    int main() {
        string name;
        double price = 0;
    
        cout << "Enter the product name: ";
        cin >> name;
        cout << "Price of the " << name << ": ";
        cin >> price;
    
        // Insert new statements here
    
        cout << "Total price: $" << price << endl;
    
        return 0;
    }
    
  • Add block comments with your name and section information to the top of the program.
  • Compile and run the starter program to make sure you entered it correctly.

    When you run the program, the output should look like this:

    Enter the product name: iPod_Nano
    Price of the iPod_Nano: 149.50
    Total price: $149.5
    

    Note the format of the numbers output for the total price. We will address this formatting issue later in the exercise.

  • Run the program again for a product with a very high cost, like a Boeing 777:
    Enter the product name: Boeing__777
    Price of the Boeing_777: 212345678
    Total price: $2.12346e+08
    

    Note the format of the numbers output for the total price. This format is called exponential notation. You may have encountered it in some of your math classes.

  • Let us correct the formatting of the total price. Enter the following code before the statement that prints the total price:
    cout << fixed             // fixed notation, not scientific
         << setprecision(2);  // show 2 decimal places
    

    These statements are referred to as the "magic formula" because they for C++ to output statements in a "standard" format. Note what each statement accomplishes. 

  • Compile and run your program again and verify the output looks like:
    Enter the product name: Boeing_777
    Price of the Boeing__777: 212345678
    Total price: $212345678.00
    
  • Let us add a constant that we will use later in our program. Enter the following code after the magic formula and before the statement that prints the total price:
    const int PERCENT = 100;
    

    A constant variable (or constant) is a variable that cannot change after being assigned a value. Using a constant lets us avoid using a vague number. 

  • Now we will add sales tax to the price of the product. Enter the following code after the constant and before the statement that prints the total price:
    double taxRate = 0;
    cout << "Enter sales tax rate (%): ";
    cin >> taxRate;
    double tax = price * taxRate / PERCENT;
    price += tax;
    

    Notice the last statement: price += tax;. This is an alternate way to code the statement: price = price + tax;.

  • Compile and run your modified program and verify the output looks like:
    Enter the product name: iPod_nano
    Price of the iPod_nano: 89.50
    Enter sales tax rate (%): 9.5
    Total price: $98.00
    
  • Now we will find the whole dollars and cents of the amount to demonstrate casting. Enter the following code after the statement that prints the total price and before the return statement:
    int dollars = (int) price;
    cout << "In whole dollars: $" << dollars << endl;
    

    Notice the (int) in the first statement. This is known as a type cast or just cast

  • Compile and run your modified program and verify the output looks like:
    Enter the product name: iPod_nano
    Price of the iPod_nano: 89.50
    Enter sales tax rate (%): 9.5
    Total price: $98.00
    In whole dollars: $98
    


Boolean Variables

  • Sometime we need to evaluate a logical condition in one part of a program and use it elsewhere
  • To store a condition that can only be true or false, we use a Boolean variable
  • Boolean variables are named after George Boole (1815-1864), a pioneer in the study of logic
  • We specify a Boolean variable using the bool type, which can hold just one of two values: true or false
    bool isCool = true;
    bool lies = false; 


Test Conditions and Boolean Values

  • Remember that test conditions always evaluate to true or false
    if (num > 0)
    
  • Thus we can use a boolean variable as a test condition
    bool isPositive = (num >= 0);
    if (isPositive)
    
  • Note that we do not need to add a relational expression to a boolean variable, like:
    if (isPositive == true) // avoid!
  • Since the boolean variable already evaluates to true or false, adding the == true is redundant
  • Likewise, we do not need to use:
    if (isPositive != false) // avoid!
  • If we want to reverse the test condition, we can use the not (!) operator
    if (!isPositive)
  • We can see the use of a Boolean variable in the following example

Example Application Using a Boolean Variable

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
using namespace std;

// For testing
int main() {
    double num;
    cout << "Enter a number: ";
    cin >> num;

    bool isPositive = (num >= 0);
    cout << boolalpha; // output true or false for bool
    cout << "The test evaluated to: " << isPositive << endl;
    if (isPositive) {
        cout << "The number was 0 or positive\n";
    } else {
        cout << "The number was negative\n";
    }

    return 0;
}

Logical Operators

Introducing &&, || and !

Watch this video and this video.

Testing Multiple Conditions

  • Sometimes we need to test for multiple conditions in our programs
  • For example, we want to test if an age is between 18 and 25
  • We need to test both that age >= 18 and age <= 25
  • One way to make the tests is with nested if statements
    int age = 0;
    cout << "Enter your age: ";
    cin >> age;
    if (age >= 18)
    {
        if (age <= 25)
        {
            cout << "Correct age!\n";
        } else {
            cout << "Wrong age!\n";
        }
    } else {
        cout << "Wrong age!\n";
    }
    
  • If the age entered is correct, like 19, we get the message, "Correct age!"
  • If the age entered is not correct, like 15, we get the message, "Wrong age!"
  • While this works, it is cumbersome to code and read
  • A better approach is to combine test conditions with logical operators

Combining Test Conditions with Logical Operators

  • logical operator, or Boolean operator, is an operator that treats operands as Boolean values (true or false)
  • C++ has several logical operators, but we only need to use three to create any possible test condition
  • These three operators are andor and not, which are discussed below
  • These logical operators are traditionally written as && (and), || (or) and ! (not)
  • Both variants are legal under ANSI C++
  • The words are easier to read but many C++ programmers still use the older form

Truth Tables for AND, OR and NOT

and (&&) Operator Truth Table
If expr1 is...And expr2 is...Then expr1 and expr2 is...ExampleResult
truetruetrue5 < 10 and 5 > 2true
truefalsefalse5 < 10 and 5 < 2false
falsetruefalse5 > 10 and 5 > 2false
falsefalsefalse5 > 10 and 5 < 2false
or (||) Operator Truth Table
If expr1 is...|| expr2 is...Then expr1 or expr2 is...ExampleResult
truetruetrue5 < 10 or 5 > 2true
truefalsetrue5 < 10 or 5 < 2true
falsetruetrue5 > 10 or 5 > 2true
falsefalsefalse5 > 10 or 5 < 2false
not (!) Operator Truth Table
If expr is...Then ! expr is...ExampleResult
truefalse!truefalse
falsetrue!(5 < 2)true

Example Using Logical Operators

  • We could rewrite our age test using an and (&&) operator like this:
    int age = 0;
    cout << "Enter your age: ";
    cin >> age;
    if (age >= 18 && age <= 65)
    {
        cout << "Adult!\n";
    } else {
        cout << "Child, Teen or Older Adult!\n";
    }
    
  • Notice that the code is shorter and it is easier to follow the logic
  • Another way to use logical operators to test the age is:
    int age = 0;
    cout << "Enter your age: ";
    cin >> age;
    if (age < 18 || age > 65)
    {
        cout << "Child, Teen or Older Adult!\n";
    } else {
        cout << "Adult!\n";
    }
    
  • Many people confuse && and || conditions, especially when learning about logical operators
  • A value lies between 0 and 100 if the value is at least 0 and at most 100
  • A value is outside that range if it is less than 0 or greater than 100
  • There is no golden rule; we have to think carefully and test our conditions

Another Look at Truth Tables

  • Note that most computers store true as 1 and false as 0
  • If we substitute 1 for true and 0 for false, we have these truth tables:

two value logic tables

  • With this substitution we see that the AND operation is the minimum of the operands
  • Conversely, the OR operation is the maximum of the operands
  • The NOT operator simply reverses its operand

Parenthesis

  • Remember that a Boolean expression in an if statement must be enclosed in parenthesis
  • Thus, an if statement with && might look like:
    if ((guess != GUESS1) && (guess != GUESS2))
  • However, relational operators have a higher precedence than logical operators
  • Thus, we can remove the inner parenthesis without affecting the meaning:
    if (guess != GUESS1 && guess != GUESS2)
  • However, if using parenthesis is easier to understand then use the extra parenthesis

Example Program with Logical Operators

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>
using namespace std;

int main()
{
    cout << "Enter true(1) or false(0) for two operands: ";
    bool op1 = false, op2 = false;
    cin >> op1 >> op2;
    cout << op1 << " and " << op2 << " = " << (op1 and op2) << endl;
    cout << op1 << " or " << op2 << " = " << (op1 or op2) << endl;

    return 0;
}

Activity 10.2: What's Your Generation? (10 pts)

  • There are 6 generations living in America, side-by-side, today.
  • Your program will determine to which generation your user belongs.
  • Find a partner for pair programming and open up a new file in CodeBlocks and name it generation.cpp.
  • To do so, you will need to take as input the year of his or her birth.
  • Then, you will need a series of test conditions (think if - else if - else) to determine the generation of your user.
  • You will also need to use logical operators (&&, ||, !).
  • Below is a chart with the range of birth years for each generation.
  • Note the double quotes around each generation's name. For full credit, you must include the " when you output the generation.
Years of Birth                    Generation
1900-1927                           "The Greatest Generation"
1928-1945                           "The Silents"
1946-1964                           "The Baby Boomers"
1965-1979                           "Generation X"
1980-1999                           "The Millennial Generation"
2000-2016                           "Generation Z"

  • Your goal is to prompt the user for his or her date of birth and then print out a message about which generation he or she belongs to.
  • To start, print the following message to the user:

    What's your generation?
  • Then, you will need a variable to store the user's year of birth:
int year_of_birth;
  • Next, prompt your user to enter his or her date of birth with a statement like the following:
Please enter the year of your birth: _
  • Subsequently, you will need 6 if and if else statements like the following:
if ( year_of_birth >= 1900 && year_of_birth < 1928)
{
    cout << "You belong to the \"Greatest Generation\"" << endl;
}
else if ( year_of_birth >= 1928 && year_of_birth < 1946)
{
    cout << "You belong to the \"The Silents\"" << endl;
}
//rest of your else ifs and your else clause go here
  • Important: Why are we using && here and not ||?
  • Finally, you will need to do some error checking of the user input.
  • If the user inputs a date that is either too high or too low, your program must print out the following message:
Invalid entry. Please enter a birth year in the range 1900 - 2016.
  • The above should go in your else clause
  • Make sure your output is identical to the sample output below before you submit.
  • When you are finished, upload to Catalyst

What's your generation?
Please enter the year of your birth: 1926
You belong to the "Greatest Generation".

Alternately,

What's your generation?
Please enter the year of your birth: 2017
Invalid entry! Please enter a birth year in the range 1900 - 2016.

More Information on Logical Operators

Conditional Pitfalls

  • Unfortunately, you can write many things in C++ that should be incorrect but end up working for some obscure reason
  • This means that you can code something that should create an error message but does not
  • Thus, a program may compile and run with no error messages but still be wrong
  • Since you may not realize that it is wrong, it can be hard to find and correct these types of errors

Strings of Inequalities

  • One common mistake is to use = when you meant to use ==
  • For example, look at the test condition in the following code:
    if (guess = 7) {
        cout << "*** Correct! ***\n";
    } else {
        cout << "Sorry, that is not correct.\n";
    }
    
  • Notice that the condition is really an assignment statement and not a test
  • You would think that it would fail to compile -- but it does not
  • However, it will not work as you might expect
  • A way to prevent this type of problem is to reverse the order of your test condition:
    if (7 = guess) {
  • Now the compiler will give you an error message and your code will not compile:
    guess.cpp: In function `int main()":
    guess.cpp:10: error: non-lvalue in assignment
    
  • However, if you correctly use == then your code will compile
    if (7 == guess) {

Strings of Inequalities

  • Do NOT use a string of inequalities like the following:
    int a = 5, b = 1, c = 10;
    if (a < b < c) {
        cout << "b is between a and c\n";
    } else {
        cout << "b is NOT between a and c\n";
    }
    
  • Your code may compile and run but give incorrect results
  • The test condition is evaluated by the computer from left to right
  • The first condition is a < b which evaluates to 0 (false)
  • The second condition is then 0 < c which evaluates to 1 (true)
  • Since the whole test condition evaluates to true you get an incorrect result
  • Instead, the correct way is to use && as follows:
    int a = 5, b = 1, c = 10;
    if (a < b && b < c) {
        cout << "b is between a and c\n";
    } else {
        cout << "b is NOT between a and c\n";
    }
    

Strings of Logical Operators

  • Logical expressions often read like "normal" English.
  • However, C++ requires more exactness than English
  • For example, the following code will compile and run but give wrong results:
    int guess;
    cout << "Enter a guess: ";
    cin >> guess;
    if (guess == 7 || 8) {
        cout << "*** Correct! ***\n";
    } else {
        cout << "Sorry, that is not correct.\n";
    }
    
  • The test condition is evaluated by the computer from left to right
  • The left hand side is (guess == 7) which can evaluate to either true or false
  • The right hand side is 8, which is interpreted as true by C++
  • Since (something or true) is always true, then the test condition always evaluates to true
  • Instead, the correct way is to use || as follows:
  • int guess;
    cout << "Enter a guess: ";
    cin >> guess;
    if (guess == 7 || guess == 8) {
        cout << "*** Correct! ***\n";
    } else {
        cout << "Sorry, that is not correct.\n";
    }



Wrap Up
  • Find a partner and answer the questions from today's learning objectives

Upcoming Assignments
  • Assignment 10 due Tuesday at 3:20pm on Catalyst
  • Lab 5 on Friday

~Have a Good Weekend!~