CSE13S c++ programme

Assignment 4
Calculator
1 Introduction
It is common knowledge that computer scientists have very few hobbies. One of the hobbies that almost every computer science major has is making worse versions of software that already exists. While there are many existing calculator applications, in this assignment, not only will you be making a basic scientific calculator, you will also be making a subset of the functions in . You will also be learning more about how computers work, and how they approximate mathematical values. Finally, you will get a chance
to test your debugging and testing skills.
2 Helpful information
Reverse Polish Notation
In your academic career, you have probably used a lot of calculators, and most of them have probably had different ways to input an expression. Because it is hard to interpret and implement a calculator that inputs
an expression the way that one might write it, we will use the standard Reverse Polish Notation. You can read more about it here, or in your C  textbook. RPN always takes numeric values first, followed by an
operator to apply. For example, the expression 2 + 2 = would be entered as 2 2 + [enter].
We can also chain these to make more complicated expressions. For example, 2 2 + 1 - is the expression (2 + 2) 1 . You’ll notice that there is no need for parentheses in RPN, as it is always explicit which numbers
are provided to which operator. With enough operators and a big enough buffer to hold previous results, it is possible to create a working, useful calculator.
RPN calculators are often implemented using a Stack ADT, which we will discuss more in the next section.
The stack ADT
To understand a Stack, we must first understand what an ADT is. ADT stands for "abstract data type".
ADT’s are simple instructions that describe the input and output of a data type, without giving any rules for how they will be implemented internally. For example, a stack ADT could be implemented with a singly linked list, a doubly linked list, an array, or many other things. Depending on the use case, each of these has its advantages, but they are all stacks. Instead of describing implementation, ADT’s only describe functionality.
A good example of a stack is a stack of pancakes on a plate. There are only two things you can do to the stack: add one pancake to the top of the stack or remove a pancake from the top of the stack. Notice that  there is no way to see or modify elements in the middle of the stack without removing all elements above it first.
A stack in its most basic form has 2 operations: Push and pop. Many versions of the stack ADT extend this functionality. To push to the stack is to add an element to the top of the stack. To pop from the stack is to remove the top element and crucially, return it to the user. Obviously, popping from an empty stack will return an error.
For simplicity, our stack implementation has a fixed capacity. This means that we decide the maximum number of elements it can contain at compile time, its capacity . This is not to be confused with its size , which is the number of elements in the stack. If a stack is at is capacity, it is not able to push an element, but it can still pop an element.
Some extensions of the stack include having a function that checks if the stack is empty (has a size of zero elements) or full (has a size of capacity elements if it is memory restricted), Printing the elements in the stack, emptying the stack, or a "peek" function, that returns a copy of the top element of the stack. The reason that peek is not part of the base stack ADT is that a peek can be performed by popping an element, returning a copy to the user, and pushing the same element back to the stack.
Libraries
Please read Ben’s guide to compilation for more info.
Command Line Options and Arguments
Please read Ben’s Guide to Options for more info.
Math
We have already covered the fact that the numeric data types in C are imprecise. You can verify this by adding .1 and .2 . While the output may look normal and correct at 16 digits, we are quickly reminded of the limitations of technology at 20 digits: the result is 0.30000000000000004 ! This occurs because floatingpoint numbers can only precisely represent values that can be written as fractions with a denominator that is a power of 2. Just like how 1 / 3 cannot be written exactly in decimal, the binary representation of fractions
like 1 / 10 have patterns that should carry on infinitely, but are limited by the precision of numeric types.
This limitation is combined with the fact that one can not store the exact value of irrational numbers on a computer (or anywhere, really). Because we are already so limited, computers use approximations of  standard math functions as well. One well known method to approximate a function is the Taylor Series.
This method approximates any function as a polynomial. As we increase the degree of the polynomial, the function becomes more accurate. The Taylor series uses a starting point, and repeatedly finds the derivatives, adding a new term with higher degree each time. This process increases the precision.
We can generalize the Taylor Series further. The Maclaurin Expansion is the Taylor Series centered  around x = 0 . Because we are only approximating functions between x = 0 and x = 2 π, we can use the Maclaurin expansion. To understand how the Maclaurin expansion of sin x works, see this link.
The formula for the Taylor Series can be written as
X n =0 f ( n) (a ) n! (x a ) n
Our first natural question should be "How can a computer take a sum to ? Wouldn’t that take forever?".
This is where we must approximate. As we sum more terms, we get an increasingly precise answer. The next natural question is "How do we know where to stop?" We can pick a very small number, ϵ ("epsilon"), and continue our summation until our the absolute value of next term is no longer greater than epsilon. All the series that we use add smaller and smaller terms in order to refine a more and more accurate answer, so when the absolute value of the term is very small we know that our approximation is very accurate. For
this assignment we set ϵ = 10 14 , which is defined in mathlib.h .
Our next simplification is turning this Taylor series into a Maclaurin Series. We can do that because the a in x a is where we center our approximation, and that will always be x = 0 . Now, we get the formula
It is possible to simplify this further. You can find a pattern in f ( n ) (0) which will allow you to simplify this significantly. 1 That pattern is alternating as follows: 0, 1, 0, -1, 0, 1 ... which can be simplified further  by removing the zeroes. This makes the final function for an approximation of sin
And the approximation of cosine
Computing tan x is much simpler. We know that the tan x = sin x cos
x . We can use this fact to find tan x without using a series. You should use this in your code. If you are concerned about dividing by zero, you
should remember that our functions will create an approximation , and therefore will never actually equal zero
Function Pointers
You already know that your C program can have pointers to data variables. When you declare char *s; double *d; then s is a pointer to a char , and d is a pointer to a double . Once your program sets these pointer variables to valid addresses, your program can “dereference” them using the * prefix operator. So *d can be used as a double value.
The C programming language also lets your program declare a pointer to a function . Such a pointer can be set to the address of function, optionally passed as a parameter, and then called, just as the function would be called.
An example is using the function apply_unary_operator() . You can call this function like apply_- unary_operator(sqrt);. You may recognize the parameter sqrt as is the name of a math function. In this context (with no parentheses after it), sqrt indicates a pointer to the sqrt() function.
Your program uses a function pointer as if it is a function name. That is, follow a function pointer with (), containing any necessary parameters, and whatever function the pointer is pointing to will be called.
In operators.h you will see two typedef lines. We use these because the programming-language syntax of function pointers is, let’s say, complicated. It is easier to declare two types binary_operator_fn and unary_operator_fn and then use these two types to declare function pointers.
So, looking at this declaration, bool apply_unary_operator(unary_operator_fn op); , the parameter op is of type unary_operator_fn . The typedef of unary_operator_fn says that op is a pointer to a function that accepts one double parameter and returns a double .
In operators.h you will see three curious array definitions. These are each 256 bytes long, so you can index them with a character. Each one has a few function pointers at various indices that you can use to run a function given a character input. You can read more about this in the operators section.

你可能感兴趣的:(c++,开发语言)