Skip to main content

How Does A Calculator Work?

A calculator has 11 numeric entry buttons (0-9 and the decimal), 4 operation buttons (addition, multiplication, division, subtraction), two clear buttons (clear entry, clear everything), and a result button (equals).

At the start of operations, the entry reads "0". The user then presses one of the buttons.  If the user presses "0", the entry continues to read "0".  If the user presses any other digit, the entry displays that digit.  The user can then compound that digit into a numeral by pressing additional digits.

The user might choose, however, to press a non-numeral button.  Let's think about what happens if the user presses one of the operation buttons.  To make things more concrete, let's list some actions the user might take in a sequence.  (For the sake abbreviation, I will include entire numerals as the end-product of a sequence of actions (entering digits) rather than the individual entries by which the numeral is compounded.)

So the user might make the following sequence of entries:

3
+
4
=

How does the calculator respond?  Let's list what we expect to find in the display screen after each entry in parentheses:

3 (3)
+ (3)
4 (4)
= (7)

Notice that the display continues to show the first entry (3) when the user presses the addition button.  It then delivers its expected behavior when the user enters an additional number, displaying "4."  (I will be playing a bit fast and loose with the distinction between input and what it represents throughout this entry, so bear with me.)  When the user presses "equals," the calculator gives the result of adding together 3 and 4.

How could it do that?  When the user finishes an entry, the user is ready to choose an operation.  Upon the user's selection, the calculator stores the entry and operation selected, then waits for additional input.  The user enters the additional input and then presses "equals" -- at which point the calculatorwill store that additional input and perform the requested operation, which takes both inputs as arguments.

In summary, then, the calculator stores information whenever the user presses an operation button.  When the user presses the "equals" button, the computer processes the information it has stored.

Even when we are considering simple sequences like the above, there are already a few things that can go wrong.  The user could make this entry:

3
+
-
4
=

Or the user could make this entry:

3
+
=

We have to figure out what the calculator should do in these circumstances.  In the first case, the user entered an operation twice.  The calculator (and its programmer) would have liked h/er to enter a number.  How should the calculator respond?  A simple solution is to have the calculator assume the user made a mistake in h/er first operation selection and wants to correct the error.  In that case, the calculator should keep what it stored the first time s/he pushed an operation button and simply modify the stored operation.  So if before the computer had something like this:

entry: 3
operation: +

Now it will have something like this:

entry: 3
operation: -

But this solution requires the calculator to be aware, whenever the user pushes an operation button, of whether or not s/he has previously pushed an operation button.  If the user has pushed an operation button, the calculator will make the requested correction (since this is how it interprets that otherwise strange behavior); if the user has not pushed an operation button (if h/er last entry was a number), it will of course proceed as normal.  Fortunately, this sort of memory is built into the functioning of our calculator as described so far: the calculator is aware when an operation has been requested, because it keeps track of what operation was requested upon request.

What about pushing equals "ahead of schedule"?  Our chief concern should be not to punish the user for this kind of mistake.  If the user presses "equals" in the wrong circumstances, we should keep the data we have stored and reset everything else.  That is, we simply return the first entry the user gave us (because we have no second entry) and wait for the user to select a different operation.

This should cover all basic use cases, so that we have a calculator that can add, subtract, multiply, or divide two numbers (though of course we don't let the calculator attempt to divide a number by 0).  Now comes the complicated part: how do we chain together multiple operations?

From the outset, we want to be able to accommodate cases like the following:

3
+
5
*
4
-
2
=

The result of the calculation should be 21 -- that is, it should follow the order of operations.  I want to think of the order of operations as organizing a number (our result) into a kind of tree pattern.  In the case of 21, for the sequence entered above, the tree looks like this:



The absolute atoms of composition are the individual entries (5, 4, 3, 2).  As we build the tree, the order of operations determines what gets grouped with what.  So we start with "3" and add it to the tree under "plus".  We then add "5" to our tree on the other side of plus.  But when we see that "times" comes after "5", we realize that "5" is part of another number, so we group "5" with that number under the entry "times" and wait for more information.  We get it in the form of "4", which completes the "times" part of the tree.  Now that we are finished with that, we continue the process of building up our "plus".  "Plus" has two entries as well, so we now have a complete number (23 if you're curious).  Then we see that we have a minus, so we wait for the next entry.  We get the next entry in the form of two and again have a complete number (21).

Where does the order of operations come in?  Well consider how the structure would differ if we got a series of entries like this:

3
+
3
-
5

In that case, our tree would be as follows:

For the sake of comparison, I've also provided an example of a tree with the same content, but different operations.  From these trees, we can see that the privileging of one operation over another happens as the tree is being constructed.  One number combined with another number by "times" and "divided by" always forms a new number, but one number combined with another number by "plus" or "minus" may not form a new number, depending on the operation that follows. 

Be that as it may, there are certain limitations to the complexity of the structures we have to deal with.  Considering now a sequence of operations, the most we're ever "waiting for" seems to be one operation.  Put it another way: at a certain point in parsing user entries, we can deliver "the result so far."  So the question is, how many operations can the user enter and still keep us waiting for a result?  I believe the answer is two.  That is, for example, the user enters "plus" and "times" -- and now we have to wait for another number.  But once we get that number, we should be done.  Whatever number the user enters after "times" will give us a complete new number, because we always consider the following sequence of entries as complete: [number], [multiplication/division], [number].  On the other hand, the user might give us this:

3
+
3
+
2

We are not going to be waiting for anything else to deliver a provisional result in that case either, because the first "plus" has been completed by two numbers.  In short, the rule can be put like this:

  1. If a multiplication or division sign is flanked by two numbers, the result is a number.
  2. If an addition or subtraction sign is flanked by two numbers, and neither of those numbers flanks a multiplication or division sign, it is complete.
So all that helps us think about what the calculator has to keep track of as it receives more and more entries.  The important point is it isn't going to have to keep track of an entire tree from beginning to end.  At any point in time, the calculator is climbing a little piece of the tree.  It keeps track of two operations and the numbers flanking those operations.  So we can imagine an algorithm like this:

If the user enters an operation, store the first entry and the operation. If the operation is multiplication or division, the user enters another entry and pushes another operation button (ideally).  When the user pushes that second operation button, the calculator applies the first operation to the entries already made, then waits for the next number (repeating the process).  If the operation is addition or subtraction, the user enters another entry and pushes another operation button (ideally).  If that next operation is addition or subtraction, the calculator proceeds as in the case of addition or multiplication and division.  But if it is multiplication or division, the calculator has to wait one more time to get a complete number, then adds that complete number to the entry it already received.  Schematically:

Case 1: entry => multiplication / division => entry => operation (compute and display result of previous operation)

Case 2: entry => addition / subtraction => entry => addition / subtraction (compute and display result of previous operation

Case 3: entry => addition / subtraction => entry => multiplication / division => entry => operation (compute and display result of previous operation).

***

So those are the basics (!) of how a calculator works.  The upshot is that someone who has reached the point where s/he can design the interface of a calculator and get it to respond to inputs appropriately is well on h/er way to designing more complicated applications.  (For mine, I'm still working out the kinks.)

Comments

Popular posts from this blog

Getting Geodata From Google's API

The apps I'm going to be analyzing are part of Dr. Charles Severance's MOOC on Python and Databases and work together according to the following structure (which applies both in this specific case and more generally to any application that creates and interprets a database using online data). The data source, in this case, is Google's Google Maps Geocoding API.  The "package" has two components: geoload.py  and geodump.py .  geoload.py  reads a list of locations from a file -- addresses for which we would like geographical information -- requests information about them from Google, and stores the information on a database ( geodata.db ).  geodump.py  reads and parses data from the database in JSON, then loads that into a javascript file.  The javascript is then used to create a web page on which the data is visualized as a series of points on the world-map.  Dr. Severance's course focuses on Python, so I'm only going to work my way through ...

Compiling and Executing Java Files With -cp

I decided I was going to "man up" and figure out how to compile a java program with an external dependency from the command line instead of relying on an IDE-- the DOS command line, to be more specific. I ran into a few problems: 1.  The external dependency was given to me as a java file.  I experimented compiling it as a .jar, but I wasn't sure how to import a class from a .jar, so I ended up compiling it into a class. 2.  When I tried to run the file, I got an error saying that the class had been compiled with a different version of Java than my JRE.  The Internet told me to check my path variable for Java.  It sure looked like it was pointing to the latest JRE (and the same version of Java as my compiler).  I asked the Internet again and found the following command: for %I in (java.exe) do @echo %~$PATH:I I'm not exactly sure what the syntax of that magic command is (intuitively it's returning the path that executes when I run the "java" com...

Quick Find / Quick Union (Connected Nodes)

Setup This week I learned about the "Quick Find" or "Quick Union" algorithm. Imagine an NxN grid of nodes, some of which are connected by lines. A connection can be interpreted as accessibility: if two nodes are connected, you can get from one to the other. Every node is accessible to itself: to get where you already are, stay there. Also, If you can get from A to B, you can go back from B to A. And if you can get from A to B and from B to C, then you can get from A to C. As a consequence, the connection between nodes divides the grid into regions of mutually accessible nodes. You can travel from any node in a given region to any other node in that region -- but not to any nodes outside that region (exercise to reader -- proof by contradiction). The problem has two parts. First, find a way to represent this grid structure and the accessibility relation; second, use your schema to efficiently calculate whether two given nodes are accessible to each other. ...