Decision Making: making programs intelligent
Have you heard of computers that beat chess champions or mimic experts? The decision making statement responsible for making a program an intelligent entity is known as the "if" statement. However, not using the proper decision-making can cause a program to fail or possibly become dumb. This is known as a computer error. An example of a program error is to print or display a paycheck for an employee who worked regular hours with a negative amount of money.
If given an employee's salary, how would you determine the tax rate? If given the number of hours an employee worked, how would you determine the overtime hours? If you were asked whether you want to continue, or to stop what you were doing, what would you answer? If you were asked to enter your password and you typed either the right or the wrong password, what would you expect to happen? If given a menu with a list of items, how would you make a selection? These are a few examples of decision-making. They are used in a similar fashion in programming.
In C/C++, decision making is done by using an if or a switch statement. The if statement uses the form shown in Figure 4.1.
|
Common Value Comparisons of IF Expression
|
|
|
equality = = |
not equal != |
|
less than < |
less than or equal <= |
|
greater than >
|
greater than or equal >= |
The result of the expression can be either true or false. In C/C++, anything besides zero is considered to be true. Therefore, the number zero is false and any other number is true. The expression if can be made more complex when making several evaluations or even an assignment statement.
The if statement syntax is illustrated by Figure 4.3a and 4.3b.




The statement after if or else can be followed by any other statement, such as an assignment, a loop, or even another if statement.
In the example below, two numbers are compared to each other to determine whether the first number is less than the second number. If the answer is true the first statement will be executed; otherwise, the statement after the else will be executed.

![]()
What happens if the two numbers are equal? In the above statement the else part will be executed because the RESULT OF THE STATEMENT firstnumber < secondnumber is not true. Therefore, the statement THE SECOND NUMBER IS THE MINIMUM will be displayed. To avoid displaying the second number is the minimum, it is possible to use an assignment to set the minimum value and then display the minimum value at the end. In this case, both are evaluated as the minimum.


![]()
![]()

When finding the minimum of two numbers and the numbers are equal; we do not want to display either as the minimum. Therefore, we need to include an additional if statement to the program of figure 4.6a to evaluate it if the numbers are equal, as illustrated in figure 4.7a.


![]()


An if statement may contain more than one statement whether its condition is true or false. Braces that make up the compound statement surround each group of statements; however, braces aren’t necessary when only one statement is placed after the condition of if or else.

FIND THE MINIMUM AND MAXIMUM OF TWO NUMBERS: USE OF A COMPOUND STATEMENT
In the example shown in figure 4.9a, the body of if and else contain more than one statement, this represents a compound statement.

![]()

![]()



Every else must match with its if. When there are multiple ifs and else, every else corresponds with its nearest if. This may look uncertain, but the compiler’s rules have been set and ambiguities have been resolved. For clarity, we use indentations to match every else with its corresponding if. By default, one statement belongs to the body of an if and one statement belongs to the body of an else. If there is more than one statement, you must put the statements within braces. Also, for clarity, you may use braces for any of the above.
The payroll program of figure 4.12a, on the next page, computes an employee’s tax rate based on the amount of gross pay according to the following parameters:
· If gross pay is more $1000, the tax rate is 30%.
· If the gross pay is more than $800.00 and less than or equal to $1000.00, then the tax rate is 20%.
WHAT IS AN ALGORITHM? WHERE DOES THE ALGORITHM COME FROM?
When it comes to programming and problem solving, the word “algorithm” is frequently used. At first glance, most people associate the word algorithm with a mathematical term such as Logarithm. In programming, algorithm refers to the problem solving steps that are needed to find a solution to a problem, such as finding the shortest path from one city to another or computing employee wages. Where does the word algorithm come from? It came from the Persian scientist Alkharazmi who wrote a book around 825 A.D. called “Algebr and Moghabeleh” which translates to “Reduction and Calculation”.





![]()
FIND THE MAXIMUM OF MANY NUMBERS: ALGORITHM EXPLANATION
How would you find the maximum of several numbers? You already learned how to find the maximum of three numbers in different ways. One thing we want to avoid is using several variables, one for each number. In fact, we want to use only two variables: one variable to hold the incoming input and another variable to hold the current maximum value, which eventually will be the final maximum. We are going to use the tournament strategy to compare every incoming number with the variable holding the current maximum. If the current number is greater than current maximum then we will reassign the maximum to the current number. We will continue to loop until all the numbers are finished. When the loop is finished, the maximum has been determined and we will display the maximum value. There is one problem that needs to be addressed: what value should the maximum contain before starting the loop and reading the input number? One solution is to initialize the maximum to the lowest possible number so that the first incoming input becomes the maximum on the first round. The steps discussed above explain the Algorithm for finding the maximum number.
Figure 4.13a, illustrates a program that finds the maximum of many numbers. Observe that the program for finding the maximum of three numbers and the program for finding the maximum of 1000 numbers or more does not differ much in the size of the program or even in the logic.



![]()

Caution: You can initialize the maximum value to 0 to start, but be sure that your data does not contain negative numbers. A different approach (algorithm) would be to initialize the maximum value by the first input data, and then start the loop by reading the next input and its comparison with current maximum value. See Figure 4.14a to view this different approach.



![]()
Table 4.2 illustrates the logical operators (AND, OR, NOT) that can be used in a program. Logical expressions, also known as Boolean expressions, result to either true or false.
|
C/C++ Logical Operators
|
|
|
Logical Expression |
Logical Operators |
|
exp1 AND exp2 |
&& ( shift + 7 Key, press twice ) |
|
exp1 OR exp2 |
|| ( shift + \ Key, press twice ) |
|
NOT exp1
|
! ( shift 1 on keyboard ) |
Many people mistakenly consider every four years to be a leap year. However, this is not the case. A leap year is any year that is divisible by 4 and not divisible by 100, or any year that is divisible by 400. The confusion arises as every 100 is divisible by 4 and every 400 is divisible by 4 and 100. Figure 4.15a shows the if condition is needed to test for a leap year.

![]()
At first, the above “logical if” can be difficult to comprehend. However, over time it becomes easier and preferable in comparison to a simple “if else” statement as illustrated below.

Figure 4.15b – Series of if statements to determine leap year
What would be the output if you run the program from Figure 4.15a with the following data?
1900 1994 1998 1999 2000 2001 2100
The year 2000 is a leap year, and a special one because it belongs to a category that occurs every 400 years. In addition to the fact that the year 2000 is a leap year, the problem of the two-digit format of the year (66 versus 1966) must be tackled. Output for the above data is shown in figure 4.15c.
![]()


The program of Figure 4.16a can be optimized with the use of an else. The result is the program of 4.16b.

In the program of Figure 4.16b, at the time the first “if statement” has failed and we are at the else, this implies that the number1 is not the maximum. Therefore, it can be excluded from further testing. Figure 4.16c shows sample input and output for the programs of 4.16a and 4.16b.
![]()

ERROR CHECKING: BULLET PROOF BUT NO SILVER BULLET
What happens when you look at the output of the payroll program and you see the net pay is a negative amount? Obviously something is wrong. A resolution is used to place an if statement within the program code that displays an error message rather than the incorrect amount. The following line of code accomplishes this task.

There is also a possibility that errors occurred at an earlier stage within the program, such as a computation error of the tax rate. For example, the tax rate was mistakenly written as the following statement, which made the net pay negative.

The taxrate variable should be assigned a value of 0.30 not 30.00. Also, the error could have stemmed from an earlier stage of data entries. Therefore, a data entry check for data validity should be included. The following segment of code addresses this issue.

How would you determine the correct range for input data that has been entered? It is not easy to test every single case of input data to make a program foolproof, especially when you write a general program without knowing your audience. A user may interact with a program in an unexpected manner. For example, instead of entering numbers, the user enters letters and the program crashes. There can be serious errors that may not be detected until the program is tested in the field and then needs to be debugged.
One thing a programmer can do is to consider the lower and the upper bounds of each data element. For example, the upper bound for hours worked would be 168 hours per week (seven days) working non-stop for 24 hours. Obviously every business has its own regulation regarding the upper and lower bound settings. For example, working 5 days a week and 12 hours each of those days provides an upper bound 60 hours.
The program of Figure 4.18a intends to find the maximum and minimum of a series of numbers. However, there is a small bug that makes the program to do something unexpected. At first glance, even an experienced programmer may not notice the error.

![]()
What is the minimum for the following input: 3 7 5 4 6. The minimum for the above input data is 3. However, due to an incorrect if and else statement, only the max value will be reset and the value of min will be skipped over. As a result the output is incorrect.


One solution to the above problem is to use two separate if statements, and eliminate the else to find the minimum and maximum. For example, lines 9 through 12 of figure 4.18a could be replaced with the following four lines of code:


Adding the four new lines of code produces the correct output as seen in Figure 4.18c.

Let us look at the following: a manager says to a programmer “We want to shut down the system when the temperature exceeds 212 degrees Fahrenheit and goes below 32 Fahrenheit.” The programmer translates the above instruction into the following code segment:

What is wrong with the above segment? The logical && requires both statements to be true, in order for the statements to perform (execute). The question that arises is how could the temperature GREATER THAN 212 and at the same time be LESS THAN 32? Obviously, the intention was to use the logical “OR” ( || ) as illustrated in figure 4.19a.

![]()

![]()
There may be more than one algorithm (approach) to solve a problem. In choosing an algorithm to solve a problem and writing the program, the criteria of speed and space have always been a concern. Another concern and important measurement is whether the program is easy to write and understand. There are situations when time is crucial and the speed of the program is very important. Also there are situations where due to the lack of available memory there is a space shortage. Obviously, programs should be user-friendly, as well as easy to write and maintain. While it may not be possible to satisfy all of the above criteria, programmers should attempt to make the program as efficient as possible.
Can you imagine waiting for your credit card to clear, or withdrawing money from the bank? Likewise, waiting for your travel agent to find a seat on an immediate flight. How long can you wait? To determine a weather forecast, thousands of computations are required because several parameters, such as wind speed and moisture need to be considered. The above are real time applications that are examples of functions of time, and each has a deadline. A programmer must choose an algorithm that fits, runs faster, and avoids unnecessary code.
SAVING MEMORY: WHY IS IT IMPORTANT?
In the early days of computing, a gigantic mainframe computer had less memory capacity than today’s personal computer. As a result of the memory limitations, a large program could not be executed all at once. A programmer had to use extra effort to solve the problem with less memory usage. As memory locations are accessed back and forth the processing time is hindered. The designs of block structured programming languages were intended to conserve memory. Even though today’s computers have enormous memory space and virtual memory, adequate use of memory is still crucial because programs are becoming proportionally larger and larger.
SAVING MEMORY YIELDS TO SPEED AND EFFICIENCY
The proper use of memory in a program can lead to a faster and more efficient program. For example, in C/C++ an integer value can utilize a data type that uses less memory. Several data types are provided, and each holds a different range of integer values. Table 4.3 shows the different types of integer values.
Integer Type |
Storage Size |
Range |
Unsigned Range |
|
short int |
1 byte (8 bits) |
-128 to 127 |
0 to 255 |
|
int |
2 bytes |
-32768 to 32767 |
0 to 65,535 |
|
long int |
4 bytes |
0 to 4,294,967,295 |
A programmer can take advantage of the above variations and select the proper type. The proper selection of a data type results in saving memory, better error checking (out of range), and speeds up operations. Moreover, one bit of the above data type holds the sign (sign bit) as to whether the number is negative (1) or positive (0). Therefore, when the data is negative it is referred to as an unsigned integer. The number of bytes for the various integer data types is machine or compiler dependent.
sizeof() OPERATOR : BYTE DETERMINATION
You can use the sizeof( ) operator to determine the number of bytes assigned for each different data type by the compiler. The following line of code will return the number of bytes the data type uses.
cout << sizeof( long ) << endl;
The output may display either 4 or 8, depending on the computer or the compiler.
Y2K PROBLEM: SAVINGS PROBLEM
In the early days of computing data was punched in eighty column cards, and read into a computer with a small memory and with slower processing time. Therefore, saving time and memory was a crucial issue and any possibility was taken into consideration. This trend continued from generation to generation, creating a new problem with each generation. The major problem of the year 2000 compliance is attributed to saving memory. For example, a year was entered as two digits (55) instead of four digits (1955).
Ironically, the memory saving of the past has become today’s problems.
GROSSPAY WITH OVERTIME: IF ELSE WITH COMPOUND STATEMENT

![]()




An alternative to the if statement is the switch statement. When there are multiple if statements, one after the other, the switch statement makes the program more legible and neater.
The switch statement uses the following general syntax where the expression is evaluated to a value. This value (integer or character) is matched against the appropriate case and the required actions will be taken upon succession. At the end the default will be executed when there is no matching case for the value.
The switch statement is not the same as the “nested if and else”. To achieve the same effect of if with else, you need to use the break keyword after the completion of each case. Figure 4.21 shows the syntax of the switch statement.

![]()
switch STATEMENT EXAMPLE: MENU



![]()
What would the average be when the value of n is zero? Division by zero is not allowed; therefore, a test should be done in advance to disallow the division by zero. The code snippet of figure 4.23 displays an error message when n is equal to zero.



CONTINUE TO LOOP
Normally when control flow reaches the bottom of the loop, it starts over again at the top of the loop. By using the keyword continue the control flow will be redirected back to the top of the loop. An example of continue, is to further the loop if the criteria has not been met. The program of Figure 4.26 will take a number as input, and determine whether the number is even or odd. However, only odd numbers are processed (e.g. looking for prime), and the even numbers are discarded and the loop continues.


![]()

if WITH goto: TO GET OUT


![]()
![]()
A SIMPLE SEARCH PROGRAM: GIVEN THE ID FIND THE PRICE
The program of Figure 4.28a illustrates a simple search program. The program shares the same fundamentals of a complicated search program. The program searches for an item’s id, and upon finding a match displays the item’s price. The input data for the program is previously created and saved in a file named itmprice.in. To illustrate the power of the operating system, the file is linked to the program using the MS-DOS command line and the indirection operator. The use of the operating system to link programs to a specific data file was discussed in the section Program And Data File Linkage: The Operating System in Chapter 3.
There is a drawback to Figure 4.28a’s program. Each time you need to search for another id, you must change the value of searchid within the program, and recompile to create another executable file that can run from the command line. To solve the problem, Figure 4.29a’s program reads the searchid as the first data element from the data file named itmprice2.in. Notice that itmprice2.in is the same data file as listed in itmprice.in used for figure 4.28a’s input, except for the first data element within the file. Although this is a step in the correct direction, it is not the final answer to solving the problem of creating a totally interactive program. After all, if you were going to search for a value, you would not want to add the value your searching for to the top of a data file. Instead, you would want to be able to search a file interactively by typing in a search parameter through the keyboard.
If the reader looks closer, there is another problem with the programs of Figure 4.28a and 4.29a. What happens if no match is found for the searchid? The program is not very user friendly in the case of not finding a match. A user would receive a blank screen.
Figure 4.30a illustrates how to solve the no match problem, as well as create an interactive program by the explicit use of naming the file to be searched. Adding line 21 of figure 4.30a solves the issue of not finding a match. The only time the line will be displayed is when the entire file is searched and there is no data element that matches the searchid. Also, the program becomes interactive with the use of the explicit opening of the file named itmprice.in. Notice line 8:
![]()
The line might look a bit strange but it is very powerful. The declaration of an input file stream variable named fin is created and the file named itmprice.in is opened. Also, notice the next line ( line 9 ):
![]()
The line looks very similar to both of the previous programs, except for the user defined file input ( fin ) variable. The variable fin assumes the role of cin, and reads information from the file named itmprice.in until the end of file is reached.
After file usage is complete, it is important to close an opened file. Line 13 and 17 indicate the proper file usage house keeping by closing the file named itmprice.in.
![]()
In order to accomplish the file reading notice line 1 of Figure 4.30a. The header file fstream.h ( meaning file stream header ) must be used. You may be asking, why did the file header iostream.h disappear? That is a good question to ask. For now, you must trust the header fstream.h when using a file. In Chapter 9, File Handling, we will discuss the fstream.h header file and many more file related tasks in greater detail.