header

Lab 8

You are to write an enhanced version of your program that evaluates an infix expression entered by the user (see homework 7). In particular, your enhanced program should handle one additional operator, six functions, an independent variable and pi (the value of π):

  1. Implement exponentiation using the ^ character where 3 ^ 2 means 32.
  2. Implement the sine function as: sin ( argument ).
  3. Implement the cosine function as: cos ( argument ).
  4. Implement the tangent function as: tan ( argument ).
  5. Implement ex as: exp ( argument ).
  6. Implement the natural logarithm as: ln ( argument ).
  7. Implement the common logarithm as: log (argument ).
  8. The infix expression may use "x" as a non-numeric operand (e.g., 3 * x ^ 2 - 4 * x + 2).
  9. The infix expression may use "pi" as a numeric operand.

Obviously, ^ is just another arithmetic operator and dealing with it presents no unusual problems. The six new functions are also operators. The difference is that arithmetic operators have two operands and functions have only one operand (which is often referred to as the function argument or, in a computer programming class, as the function parameter). Dealing with a non-numeric operand presents some challenges that we will deal with below.

The precedence Function

Exponentiation has higher precedence than multiplication and division. For example, 3 * 4 ^ 2 = 3 * ( 4 ^ 2 ) = 3 * 16 = 48. A function has a higher precedence than exponentiation since functions must be evaluated before they can be used in other arithmetic operations.

The convertToPostfix Function

The function to convert the infix expression to a postfix expression does not need to be modified in any way to handle the new operator or the functions. However, it will have to be modified to handle x, the non-numeric operand. In your previous program, any token that did not represent a number was an operator. Now we will have to make an additional test. If a token is not numeric then we must test to see if the token text is "x". If so, the token still represents an operand. However, if we set the isOperand field of a token to true, then we expect to find the value of the operand in the operand field of the structure. But this is a non-numeric operand so how do I represent a non-numeric operand in the numeric operand field?

The format used to store double values has a special bit-pattern known as NaN (not a number). For example, the statement "cout << sqrt(-1)" will output the text "NaN" indicating that the square root of -1 is not a number. In the cmath header file, the constant "NAN" is a double-type constant whose value is the NaN bit-pattern. This is the double value that we will store in the operand field of any token that corresponds to the non-numeric operand.

Our modified logic goes like this. If the token string is a number then set isOperand to true and store the number in the operand field (as we did before). Otherwise, if the token string is "x" then set isOperand to true and store NAN in the operand field (this is new). Otherwise, set isOperand to false and store the operator in the op field of the token (as we did before).

Dealing with "pi" is even easier. If the token string is "pi" then isOperand is set to true and the value of π is stored as the operand value.

The valueOf Function

You will need to make two minor changes to the valueOf function to accommodate these new features. First, you must add one new parameter to the valueOf function. This will be double parameter that represents the value of x. The new function prototype is

double valueOf(list<tokenType>& postfix, double x)

You will also have to make a minor change in your logic. Before, if the isOperand field of a token was true, all you had to do was push the value in the operand field onto the stack. Now, before you do that, you will need to check to see if the value in the operand field is NaN. If it is, then the token corresponds to the x variable and you need to push the value x onto the stack. Otherwise, you push the value of the operand field onto the stack as you did before.

Now, you might think that testing to see if the value in the operand field is NaN would be as easy as "operand == NAN". But you would be wrong! Consider the following selection structure:

if (NAN == NAN)
{
    cout << "true\n";
}
else
{
    cout << "false\n";
} 

While you would expect the output generated by this statement would be "true", it is actually "false". That's right, NAN == NAN is false. However, the cmath header file contains a function named "isnan" that returns true if its double argument is not a number and false otherwise. This code generates the output "true":

if (isnan(NAN))
{
    cout << "true\n";
}
else
{
    cout << "false\n";
} 

If the isOperand field of a token is true, you'll need to implement the following logic:

if (isnan(value of operand))
{
    push (value of x);
}
else
{
    push (value of operand);
}

A Suggested Strategy

Few students ever learn the art of incremental programming. The common tendency is to try and incorporate every feature in your program until you can get it to compile and then worry about finding and fixing logic errors. This approach usually leads to frustration because there are so many potential sources of errors that it is very difficult to find and fix them.

I strongly encourage you to approach this lab incrementally. First, implement nothing new but the exponentiation operator which is the easiest change to implement. Don't do anything else until your modified program properly handles exponentiation.

Once you have added the exponentiation operator, add just one function to your program. For example, make the changes necessary for your program to evaluate expressions that include the sine function. Don't do anything else until your program properly handles the sine function. Then, and only then, add the code needed to support the other five functions. Once you have figured out how to do one function, the rest should come easily.

Only when your program can properly handle all six functions should you try to incorporate the independent variable x. When you have added and tested the code  to deal with x, you can deal with pi.

Project Submission

I've done some of the work for you in Lab08.cpp. All you have to do is copy the code for your precedence, infixToPostfix, and valueOf functions from your Hmwk07.cpp file into this file and then make any necessary modifications. If you make any changes to your stack or list implementations, you'll need to send those along as well (unless you place them within the Lab08 file.