Projects

Calculator Project: Explained

If you haven’t read my blog about my calculator project, then I suggest you read that first and return to this blog. If you have, then enjoy!

Though I am a junior high school student, my goal is to learn complex computer concepts and explain them in basic terms for all who wish to learn computer concepts out there. Hence the reason for this site. This blog’s aim is alike. I will explain in general terms how my previously made web calculator works and hopefully you’ll have learned something from it.

The Expression

In my calculator, I named the panel for the calculator’s output expression. In the expression is where all the numbers and operators are imputed.

The expression is found at the top of the calculator itself.

Every button is linked to a function in the main.js file. The numbers, operators and the decimal button are linked to the addValue function. The problem with the expression is that it is a string. Therefore, every time I click an operator more than twice, it outputs something like this.

12+*–5

Of course this expression is unacceptable. So I thought that every time the user clicks on a number, the user is allowed to input a new operator. However, if an operator is clicked the expression will deny any new operator. This in turn solved my problem for the numbers and operators part.

I used Booleans to help determine if a button has been clicked. I named these variables operatorMode, decimalMode, and negativeSign. The operatorMode variable will be equal to true when an operator button has been clicked. And will turn false when a number has been clicked. Signaling the expression to allow a new operator to enter into the expression. The same concept is true for decimals. Later on we’ll discuss on the negativeSign. However, the real challenge was for me to be able to add a decimal point.

The Decimal Point

I needed to devise an effective algorithm to sort out the decimal point. Here’s an example:

53.57 + 83.35.34523..353.

Every time the user inputs a decimal point, the expression should deny a new decimal point within the number. However, once a new number is clicked right after the decimal point, the expression adds a new decimal point. So I made a function that would find a decimal point within the number. It looks something like this.

function computeIfDecimalMode(value) {
let decimalCount = 0;
 for (i = value.length - 1; i >= 0; i--) {
  let evalChar = value.substr(i, 1);
	if (evalChar == ".") {
		decimalCount++;
	} else if (evalChar == "+" || evalChar == "-" || evalChar == "*"
				|| evalChar == "/") {
			break;
		}
	}

	if (decimalCount == 0)
		return false;
	else
		return true;
}

Basically, what this function does is take the current expression and check each character backwards until it reaches an operator. If there is indeed a decimal point already within the number, then the expression will not allow a new decimal point. Else if there isn’t a decimal point within the number, the expression will allow the new decimal point.

This may seem like a straightforward concept but at first it was a real challenge. I actually answered my teacher’s rule last. The rule was to limit the number of characters in the expression to 15. This was the easiest part of the calculator though. It looks something like this.

if (expression.value.length >= 15) {
 alert("Warning! There are too many characters!");
}

All I had to do was take the length of the current expression and check whether or not it is greater than or equal to 15 characters. Now I wasn’t quite done with the calculator so I added some unique features because I had some free time at the time and I wanted to see what I can do with my knowledge in programming.

Integers

I opted to allow the expression to insert negative numbers as well as positive numbers. The problem was that the negative sign is the same sign for the minus sign, a hyphen. Also, the expression requires a parenthesis before I’m able to solve it. By the way, I used the so-called “eval” method to easily compute the expression. So that is how the solve function actually solves it.

So the first thing I did was to allow an integer at the first part of the expression. Like this:

else if (val === "-" && operatorMode && !negativeSign) {
		expression.value = expression.value + "(" + val;
		operatorMode = true;
		decimalMode = true;
		negativeSign = true;
	}

This says that if there is already an operator at the end of the expression, then allow for a minus sign. This is also true for when the user wants to input an integer at the beginning of the expression. However, I still needed to be able to close the parenthesis. Thus I checked whether or not a number is negative. If the number is negative, then add “)” and then the operator sign. Else if the number is positive, just add the operator sign. The code looks like this:

else if (isOperator(val) && !operatorMode && negativeSign) {
	expression.value = expression.value + ")" + val;
	operatorMode = true;
	decimalMode = true;
	negativeSign = false;
}

This indeed was a success. But I still wanted to add one more feature. This feature was the memory. However, I created the delete and clear all functions first before doing the memory button.

The Delete Function

Now that I was able to add real numbers into the expression, I started to work on the delete function. This delete function was another one of my challenges. I needed to know what character the user was deleting and what the character at the end of the expression will be to determine the outcome of the expression. For example:

500 * (- 67.5

The expression isn’t complete but let’s say the user wants to delete the last number, 5. If the expression isn’t aware of the last character after deletion, then some problems would occur. It is true that the computeIfDecimalMode function will be able to capture this. However, if the user continues to delete the expression up to the negative sign, the parenthesis will be left over. Then the user might be able to add a new operator. The negative sign along with the parenthesis beside it should be deleted altogether. So I checked for the last character after the deleted character to determine what to do in certain scenarios. It looks like this in code:

function delBack() {
 let currentDisplayValue = expression.value;
 let newDisplay = deleteLastCharacter(currentDisplayValue);
 let lastCharAfterDelete = newDisplay.substr(newDisplay.length - 1, 1);
	if (isOperand(lastCharAfterDelete)) {
           .....
	} else if (lastCharAfterDelete === "(") {
		expression.value = deleteLastCharacter(newDisplay);
		operatorMode = true;
		decimalMode = true;
		negativeSign = false;
	} else if (lastCharAfterDelete === ")") {
		expression.value = deleteLastCharacter(newDisplay);
		operatorMode = false;
		decimalMode = true;
	.....
}

Now I’ve solved the delete function. The deleting of characters was simple. All I did was take the current expression and substring it leaving out the last character. Then I replaced the current expression with the newly updated expression.

The Clear All Button

This was one of the easiest parts of the calculator. All I had to do was empty the expression and reset the values of operatorMode, decimalMode, and negativeSign.

function clearAll() {
	expression.value = "";
	operatorMode = true;
	decimalMode = true;
	negativeSign = false;
}

The Memory Buttons

There are three parts to the memory button. These are the store, recall, and cancel functions. The aim of the memory buttons is to be able to store proper memory, recall to expression when possible, and to delete the memory when needed to by the user.

1. The Store Function

Storing the answer to the expression is the easier part. All I had to do was store the current expression’s value in a new variable. The slightly harder part was to determine whether or not the memory is a negative or positive integer. And if so, is there a decimal point or operator within it. This can all be answered through these lines of codes.

let memoryMode = false; // lets the memory buttons decide to cache the given
// expression.
let Memory; // the variable where the expression is stored.
let negativeMemory = false; // used if the memory is a negative operand.
let sign = "";

This tells the memory that if the answer is negative, then the negativeMemory variable is true. Else if the answer is positive, then negativeMemory false. There are other scenarios I encountered but this was just the slightly challenging part.

To check for the operators however, I used the same algorithm for finding out a decimal points position in a number. Instead of finding the decimal point however, I used the algorithm to find if there are any operators within the expression. If there is, the memory won’t allow for it unless it is a negative number.

Finding out if there was a decimal point in the expression was easier since I already had an algorithm to find a decimal point in the expression. However, the decimal point wasn’t much of a threat to the memory since decimal points are part of a number.

2. The Recall Function

This was the hardest part of the memory function because I had to know the current expression’s value and the memory stored. There are multiple scenarios that can be played out especially that there are now negative numbers in play. I had to first figure out the state of the memory. Is it negative or positive? If it’s negative then add an opening parenthesis at the beginning of the memory before recalling to expression. If it’s positive than add the memory as is. However, I cannot call the memory without the expression ending in an operator. Hence the reason I need to know the current expression’s value and the state of the memory at the same time. All of this can be solved with the following lines of code:

function memoryRecall() {
 if (!memoryMode) {
	alert("There is no memory!");
} else if (!checkLastCharacter() && expression.value.length > 0) {
	alert("Please add an operator before recalling memory.");
} else if (expression.value.length + Memory.length >= 15) {
	alert("Cannot add memory because it would go over 15 characters.");
} else if (checkLastCharacter() && !isOperator(sign)) {
	alert("The expression ends with a decimal point.");
} else if (checkLastCharacter() && isOperator(sign) && negativeSign) {
	alert("There is a negative sign at the end of the expression.");
} else if (expression.value.length == 0 && !negativeMemory) {
	expression.value += Memory;
	operatorMode = false;
	decimalMode = computeIfDecimalMode(expression.value);
	negativeSign = false;
} else if (expression.value.length == 0 && negativeMemory) {
	expression.value = expression.value + "(" + Memory;
	operatorMode = false;
	decimalMode = computeIfDecimalMode(expression.value);
	negativeSign = true;
} else if (checkLastCharacter() && isOperator(sign) && !negativeMemory
			&& !negativeSign) {
	expression.value += Memory;
	operatorMode = false;
	decimalMode = computeIfDecimalMode(expression.value);
	negativeSign = false;
} else if (checkLastCharacter() && isOperator(sign) && negativeMemory
			&& !negativeSign) {
	expression.value = expression.value + "(" + Memory;
	operatorMode = false;
	decimalMode = computeIfDecimalMode(expression.value);
	negativeSign = true;
	}
}

Of course there are other scenarios that can be expressed but the main idea you should know is that there must be an operator at the end of the expression’s value before recalling memory to expression.

3. Memory Cancel

Similiar to the clear all function, the memory cancel function was the easiest to set in order. All I needed to do was set the memory variable equal to “” and reset memoryMode to false. Like so:

function memoryCancel() {
 if (memoryMode) {
	Memory = "";
	memoryMode = false;
	document.getElementById("box2").value = "";
} else {
	alert("There is no memory!");
 }
}

And we’re done! This project really tested my logic in programming. Because there were many possible scenarios I had to think of them all. This project got me a 50/50 score in ICT and an additional 5 points for the additional features.

Comment below what you think about this project or if you have any questions 🙂 And if you have any suggestions of course, feel free to comment down below 😉

Leave a Reply

Your email address will not be published. Required fields are marked *