Thread: Calculator program

  1. #1
    Registered User
    Join Date
    Sep 2006
    Location
    Beaverton, Oregon, United States
    Posts
    176

    Calculator program

    As an exercise an internet programming friend gave me a create a calculator program task.

    The task is to take a character string, whether as input or predefined and calculate it out like a calculator would. For instance a character string:

    "24 * 32 * 5 + 9 / 2 -7"

    And parse it out so that it comes out with the answer of 3837.5.

    So as IMO first thing first is to seperate the operands and the character integers into two seperate strings.

    Ran into something curious yanking out the operands into their own char array.

    Code:
    // Calculator Program. Let's Dance!
    
    #include <stdio.h>
    #include <stdbool.h>
    
    
    int main(void)
    {
    	bool multDivide;	
    	int numbers[100],numCount,i,operandCount=0; // Make sure variable 'i' does not lose value incremented to it.
    
    	char workString[25]="24*5*3+12*32-19";
    	char temp[4];
    	char operations[6];
    
    	printf("Original junk in operations string: %s",operations);
    
    	for(i=0;workString[i] !='\0';i++)
    	{
    		if(workString[i]=='*' || workString[i]=='\\' || workString[i]=='+' || workString[i]=='-')
    		{
    			operations[operandCount]=workString[i];
    			operandCount++;
    		}
    	}
    
    	printf("\nOperators found:%s",operations);
    	getchar();
    
    
    }
    I was getting what appeared to be workString stuff in the operand string. So I had it print out the junk contents of the operand string first to see where this stuff might be originating from. It seems to be in there initially and going beyond its 6 or so character definition.

    Code:
    Original junk in operations string: ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠24*5*3+12*32-19
    Operators found:**+*-╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠24*5*3+12*32-19

    Is this a flaw in the %s command?

  2. #2
    Registered User
    Join Date
    May 2010
    Posts
    4,632
    Is this a flaw in the %s command?
    No, The first instance "Original junk" is because you failed to initialize your array so it contains garbage. The second line is because you did not properly terminate your C-string with the end of string character '\0'. The "%s" specifier will print everything until it finds this end of string character.
    After your loop you need to set the next character to the end of string character '\0'. Be careful that you don't try to access your array out of bounds. What happens if you have a couple of more operations in your string?

    Jim

  3. #3
    Registered User
    Join Date
    Sep 2006
    Location
    Beaverton, Oregon, United States
    Posts
    176
    Quote Originally Posted by jimblumberg View Post
    No, The first instance "Original junk" is because you failed to initialize your array so it contains garbage. The second line is because you did not properly terminate your C-string with the end of string character '\0'. The "%s" specifier will print everything until it finds this end of string character.
    After your loop you need to set the next character to the end of string character '\0'. Be careful that you don't try to access your array out of bounds. What happens if you have a couple of more operations in your string?

    Jim
    On the first string I expected garbage because I did not initialize it as you said. But what was curious to me was why it was exceeding the 6 character limit. Probably should of clarified that. It also seems to be spitting out contents in the workString array. So that must mean the compiler ordered them one right next to the other in its memory.

    ook. thanks for the help. I'll be back in this thread later. I have other questions with stuff Im running into with this program.

  4. #4
    Registered User
    Join Date
    Sep 2006
    Location
    Beaverton, Oregon, United States
    Posts
    176
    ok stumped again.

    Code:
    // Calculator Program. Let's Dance!
    
    #include <stdio.h>
    #include <stdbool.h>
    
    
    int main(void)
    {
    	bool multDivide;	
    	int numbers[100],numCount=0,operandCount=0,tempCount=0,i,j; // Make sure 'numCount' does not lose value incremented to it.
    
    	char workString[25]="24*5*3+12*32-19";
    	char temp[25]={'\0'};
    	char operations[6];
    
    	printf("Original junk in operations string: %s",operations);
    
    	for(j=0;workString[j] !='\0';j++)
    	{
    		if(workString[j]=='*' || workString[j]=='\\' || workString[j]=='+' || workString[j]=='-')
    		{
    			operations[operandCount]=workString[j];
    			operandCount++;
    		}
    	}
    
    	printf("\noperandCount value:%i",operandCount);
    	operations[operandCount]='\0';
    
    	printf("\nOperators found:%s",operations);
    	getchar();
    
    	printf("Original temp array: %s\n", temp);
    
    	printf("Parsing out character integers...\n");
    
    	do
    	{	
    		
    			if(workString[numCount]>='0' || workString[numCount]<='9')
    				{
    					temp[tempCount]=workString[numCount];
    					tempCount++; 
    				}
    				
    		numCount++;
    						
    	}while(workString[numCount]!='\0');
    
    	temp[tempCount]='\0';
    
    	printf("temp array: %s",temp);
    
    getchar();
    		
    		
    }
    The last part should just be extracting the character integers and for some reason beyond me its not working.

    I get this:

    Code:
    Original junk in operations string: ╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠╠
    operandCount value:5
    Operators found:**+*-
    Original temp array:
    Parsing out character integers...
    temp array: 24*5*3+12*32-19
    Thanks for you guyses input.

  5. #5
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Code:
    if(workString[numCount]>='0' || workString[numCount]<='9')
    Think very carefully about this line, and tell me exactly when this is false.

    Better yet, use the isdigit function provided by ctype.h.

  6. #6
    Registered User
    Join Date
    Sep 2006
    Location
    Beaverton, Oregon, United States
    Posts
    176
    Quote Originally Posted by anduril462 View Post
    Code:
    if(workString[numCount]>='0' || workString[numCount]<='9')
    Think very carefully about this line, and tell me exactly when this is false.

    Better yet, use the isdigit function provided by ctype.h.
    I was thinking real carefully about when this line was false for about 3 hours today. I'll give her another go.

  7. #7
    Algorithm Dissector iMalc's Avatar
    Join Date
    Dec 2005
    Location
    New Zealand
    Posts
    6,318
    Quote Originally Posted by A34Chris View Post
    On the first string I expected garbage because I did not initialize it as you said. But what was curious to me was why it was exceeding the 6 character limit. Probably should of clarified that. It also seems to be spitting out contents in the workString array. So that must mean the compiler ordered them one right next to the other in its memory.
    Yep. Printf knowns nothing about your array sizes. All it gets is a pointer to the start of the array and its job is to print every character from that address until it finds a character that is zero (the nul-terminator).
    My homepage
    Advice: Take only as directed - If symptoms persist, please see your debugger

    Linus Torvalds: "But it clearly is the only right way. The fact that everybody else does it some other way only means that they are wrong"

  8. #8
    Registered User
    Join Date
    Sep 2006
    Location
    Beaverton, Oregon, United States
    Posts
    176
    Quote Originally Posted by anduril462 View Post
    Code:
    if(workString[numCount]>='0' || workString[numCount]<='9')
    Think very carefully about this line, and tell me exactly when this is false.
    This is how I'm reading that line. If workstring[subscript] ascii value is greater than or equal to the ascii value of '0' OR if workString[subscript] ascii value is less than or equal to the ascii value of '9'. So it should turn up FALSE anytime the value is anything other than in the ascii character integer range?

    Better yet, use the isdigit function provided by ctype.h.
    I'd rather not just yet. I'll use homespun functions for now as its something I need to learn. Especially if I'm not seeing what's wrong with the above line. If I just used the predone libraries I really wouldnt learn as much.

  9. #9
    Registered User
    Join Date
    Sep 2006
    Location
    Beaverton, Oregon, United States
    Posts
    176
    Haha I got it! I didnt need to OR it. I needed to AND it. lol. ok thanks guys.

  10. #10
    Registered User
    Join Date
    Sep 2006
    Location
    Beaverton, Oregon, United States
    Posts
    176
    Slow going for me but its coming along. I know I am probably doing this the most inefficient way possiblebut after I'm done with it this way I wouldnt mind someone showing me an example of how to do it even less sloppy.

    Code:
    // Calculator Program. Let's Dance!
    
    #include <stdio.h>
    #include <stdbool.h>
    
    
    int strToInt (const char string[])
    {
    	int i=0, intValue, result=0;
    	bool negate = false;
    
    	if(string[0]=='-')
    	{
    			i=1;
    			negate=true;
    	}
    	
    
    
    	for(;string[i]>='0' && string[i]<= '9'; i++)		
    	{
    		if(string[i]=='.')
    		{
    			i++;
    		}
    		intValue = string[i] - '0';
    		result = result * 10 +intValue;
    	}
    
    		if(negate)
    		{
    			result= -result;
    		}
    
    	return result;
    
    }
    
    int main(void)
    {
    	bool multDivide;	
    	int numbers[100],numbersArrayCount=0,numCount=0,operandCount=0,tempCount=0,i,j;
    	int multDivideResult,multDivideResultPosition=0,answersArray[20];
    
    	char workString[25]="24*5*3+12*32-19";
    	char temp[25]={'\0'};
    	char operations[6];
    
    	for(j=0;workString[j] !='\0';j++)
    	{
    		if(workString[j]=='*' || workString[j]=='\\' || workString[j]=='+' || workString[j]=='-')
    		{
    			operations[operandCount]=workString[j];
    			operandCount++;
    		}
    	}
    
    	printf("\noperandCount value:%i",operandCount);
    	operations[operandCount]='\0';
    
    	printf("\nOperators found:%s",operations);
    	getchar();
    
    	printf("Parsing out character integers...\n");
    
    	do
    	{	
    		// Check and see if current char is an int char, if so add to temp string.
    		if(workString[numCount]>='0'&& workString[numCount]<='9')
    				{
    					temp[tempCount]=workString[numCount];
    					tempCount++; 
    					numCount++;
    				}
    
    			else if (workString[numCount]<'0' || workString[numCount]>'9')
    				{
    					// end of a int char string found. Send to function for conversion. 
    					temp[tempCount]='\0';
    					tempCount=0;
    					numbers[numbersArrayCount]=strToInt(temp);
    
    					numbersArrayCount++;
    					
    
    						do // Get past non integer characters.
    						{
    							numCount++;
    						}while(workString[numCount]<'0' || workString[numCount]>'9');
    
    				} 
    
    
    		
    	}while(workString[numCount]!='\0'); 
    
    	temp[tempCount]='\0'; 
    
    	for(i=0;i<=numbersArrayCount-1;i++)
    		printf("%i\n",numbers[i]);
    
    	Printf("Beginning calculations...\n");
    
    	multDivide=false;
    
    		do
    		{
    			if(operand[i]=='*' || operand[i]=='/')
    			{
    				if(multDivide==true)// Check for consecutive multiplication or division flag
    				{
    					n = result;
    				}
    					else
    					{
    						n = numbersArray[i];
    					}
    
    				switch(operations[i])
    				{
    				case '*':
    					result= n * numberArray[i+1];
    					break;
    
    				case '/':
    					result= n / numbersArray[i+1];
    					break;
    
    				default:
    					break;
    
    				}
    					// Check to see if consecutive mult/divide problems coming up. 
    					if((operations[i]=='*' || operations[i]=='/') && (operations[i+1]=='*' || operations[i+1]=='/'))
    					{ 
    						multDivide=true;
    					}
    					
    						else
    						{
    							multDivide=false;
    						}
    
    					
    
    					i++;
    				
    
    				}
    				
    				// Stop counter of multDivide result position in final array from incrementing willy nilly
    				if((multDivide !=true)||(countStop<=1))
    					{
    						// answersArray[multDivideResultPosition]=result;					
    						multDivideResultPosition++;
    					}
    				printf("multDivide positions:%i",multDivide
    
    		}while(operand[i]!='\0');
    
    getchar();
    		
    		
    }

  11. #11
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Code:
    $ make calc
    gcc -Wall -g -std=c99  -lssl -lm -lpthread -lcurses -lefence  calc.c   -o calc
    calc.c: In function ‘main’:
    calc.c:102: warning: implicit declaration of function ‘Printf’
    calc.c:108: error: ‘operand’ undeclared (first use in this function)
    calc.c:108: error: (Each undeclared identifier is reported only once
    calc.c:108: error: for each function it appears in.)
    calc.c:112: error: ‘n’ undeclared (first use in this function)
    calc.c:112: error: ‘result’ undeclared (first use in this function)
    calc.c:116: error: ‘numbersArray’ undeclared (first use in this function)
    calc.c:122: error: ‘numberArray’ undeclared (first use in this function)
    calc.c:152: error: ‘countStop’ undeclared (first use in this function)
    calc.c:43: warning: unused variable ‘answersArray’
    calc.c:43: warning: unused variable ‘multDivideResult’
    make: *** [calc] Error 1
    Spend a little more time making it compile. I can't tell if your program even works if I can't compile it. If it doesn't work, there's no point in trying to make it more efficient. That being said, it does not look very efficient and probably wont be correct, even once you fix the compiler errors. Some things you should consider:

    • How do you tell the difference between a subtraction operator and a negative sign? Is that method deterministic? That is, will it always work correctly?
    • Why are you ignoring decimal places inside numbers?
    • Are you using forward or back slash for division?
    • You don't appear to be handling addition or subtraction.
    • Do you plan on supporting parentheses?


    Without support for parentheses, the easiest way I see of doing this involves passing over the original string once, reducing all the multiplications and divisions down to single numbers, and generating a second, temporary string from that. Then you pass over that and reduce all the additions and subtractions. You should be left with a single number.

  12. #12
    Registered User
    Join Date
    Jun 2011
    Posts
    88
    You have the wrong symbol for divide should be / not \

  13. #13
    Registered User claudiu's Avatar
    Join Date
    Feb 2010
    Location
    London, United Kingdom
    Posts
    2,094
    This kind of development strategy of stabbing in the dark is a very poor approach to this kind of problem. I suggest you spend some time thinking about HOW you are going to solve the problem (i.e. the algorithm) rather than how you are going to code it in C.
    1. Get rid of gets(). Never ever ever use it again. Replace it with fgets() and use that instead.
    2. Get rid of void main and replace it with int main(void) and return 0 at the end of the function.
    3. Get rid of conio.h and other antiquated DOS crap headers.
    4. Don't cast the return value of malloc, even if you always always always make sure that stdlib.h is included.

  14. #14
    Registered User
    Join Date
    May 2012
    Posts
    505
    You've got the wrong approach. This is in the nature of the problem. You can't build a parser by doing a few bottom-up atomic operations right then gradually adding functionality. You have to get the whole framework right.

    Firstly you need to convert the input stream of characters into tokens. A token is an operator, a parenthesis, or a number. Operators are easy to tokenise because they are always one ascii character which can't have any other meaning (with the exception of unary minus, which we'll leave for now). Same goes for parethesies. Numbers are more complex, but you've got the strtod() function built in. At the toeknisation stage, you can reject most of the ill-formed input.

    So convert the input string into a stream of tokens and, for numbers, associated values. So it looks 2 + 2 * 3 like this
    Code:
     {NUMBER 2}  {PLUS [blank]} {NUMBER 2} {TIMES [blank]} {NUMBER 3}.
    Now we've got three levels. expressions, terms and factors. They're mutually recursive, because a factor can be either a NUMBER, or OPENBRACKET an expression CLOSEBRACKET. So factor has to call expression. Then you can have as many levels of nested parentheses as you've got room for on the call stack. A term is a set of factors strung together with * or /, but often, remmber, a term will be only one factor, with zero *s or /s. An expression is a list of terms strung together with + and -.
    So you need one unit of lookahead in your token stream. expression calls term calls factor. Facor looks at the token, if its a number it returns the value, if it's an open bracket it oextracts it from the stream, calls expression, extrtacts the close bracket, and returns the value of the expression. Term calls factor, looks at the next token. If it's a * or a /, it calls factor again, does the calculation. If it's a plus or - or end on input, it returns the value it has got. Expression calls term, looks ahead, if it sees a + or a minus, calls term again, and adds or subtracts the terms. When it reaches end of inout of a close bracket, it returns.

    If you go to my website you'll see a series of webpages about a Basic interpreter. Writing a full Basic interpreter is in principle no different to writing a expression parser.
    Malcolm McLean's Homepage

  15. #15
    Registered User
    Join Date
    Sep 2006
    Location
    Beaverton, Oregon, United States
    Posts
    176
    Quote Originally Posted by claudiu View Post
    This kind of development strategy of stabbing in the dark is a very poor approach to this kind of problem. I suggest you spend some time thinking about HOW you are going to solve the problem (i.e. the algorithm) rather than how you are going to code it in C.
    I did put some thought into it. I'm just not great at algorithms. I figured I would start with one problem at a time. I figured I'd seperate out the operands and the numbers into seperate arrays as you see here. Its the only way I could think of to do it. I'm still struggling with this.

    Latest source with corrections:

    Code:
    // Calculator Program. Let's Dance!
    
    #include <stdio.h>
    #include <stdbool.h>
    
    
    int strToInt (const char string[])
    {
    	int i=0, intValue, result=0;
    	bool negate = false;
    
    	if(string[0]=='-')
    	{
    			i=1;
    			negate=true;
    	}
    	
    
    
    	for(;string[i]>='0' && string[i]<= '9'; i++)		
    	{
    		if(string[i]=='.')
    		{
    			i++;
    		}
    		intValue = string[i] - '0';
    		result = result * 10 +intValue;
    	}
    
    		if(negate)
    		{
    			result= -result;
    		}
    
    	return result;
    
    }
    
    int main(void)
    {
    	bool multDivide;	
    	int numbers[100],numbersArrayCount=0,numCount=0,operationsCount=0,tempCount=0,i,j,n;
    	int multDivideResult,multDivideResultPosition=0,answersArray[20],result,countStop=0;
    
    	char workString[25]="24*5*3+12*32-19";
    	char temp[25]={'\0'};
    	char operations[6];
    	
    	// Pluck out all operations characters.
    	for(j=0;workString[j]!='\0';j++)
    	{
    		if(workString[j]=='*' || workString[j]=='\\' || workString[j]=='+' || workString[j]=='-')
    		{
    			operations[operationsCount]=workString[j];
    			operationsCount++;
    		}
    	}
    
    	printf("\noperationsCount value:%i",operationsCount);
    	operations[operationsCount]='\0';
    
    	printf("\nOperators found:%s",operations);
    	getchar();
    
    	printf("Parsing out character integers...\n");
    
    	printf("%s",workString);
    
    	do
    	{	
    		// Check and see if current char is an int char, if so add to temp string.
    		if(workString[numCount]>='0'&& workString[numCount]<='9')
    				{
    					temp[tempCount]=workString[numCount];
    					tempCount++; 
    					numCount++;
    					
    				}
    
    			else if (workString[numCount]<'0' || workString[numCount]>'9')
    				{
    					// end of a int char string found. Send to function for conversion. 
    					temp[tempCount]='\0';
    					tempCount=0;
    					numbers[numbersArrayCount]=strToInt(temp);
    					numbersArrayCount++;
    					
    
    						do // Get past non integer characters.
    						{
    							numCount++;
    						}while(workString[numCount]<'0' || workString[numCount]>'9');
    						
    						printf("\nNumbersArrayCount=%i\n", numbersArrayCount);
    				} 
    
    
    		
    	}while(workString[numCount]!='\0'); 
    
    	temp[tempCount]='\0'; 
    
    	for(i=0;i<=numbersArrayCount;i++)
    		printf("%i\n",numbers[i]);
    
    	printf("Beginning calculations...\n");
    	
    	// Do all multiply/Divide problems first and put the result in the proper place of the answer array.
    	multDivide=false;
    	i=0;
    
    		do
    		{
    			if(operations[i]=='*' || operations[i]=='/')
    			{
    					if(multDivide==true)// Check for consecutive multiplication or division flag
    					{
    						n = result;
    					}
    
    					else
    					{
    						n = numbers[i];
    					}
    
    
    				switch(operations[i])
    				{
    				case '*':
    					result= n * numbers[i+1];
    					break;
    
    				case '/':
    					result= n / numbers[i+1];
    					break;
    
    				default:
    					break;
    
    				}
    
    					
    				printf("\nValue of result is %i.\n",result);
    
    					// Check to see if consecutive mult/divide problems coming up. 
    					if((operations[i]=='*' || operations[i]=='/') && (operations[i+1]=='*' || operations[i+1]=='/'))
    					{ 
    						multDivide=true;
    						countStop++;
    					}
    					
    					else
    					{
    						multDivide=false;
    					}
    
    					if(multDivide !=true) // insert CountStop flag in here somewhere.
    					{
    						answersArray[multDivideResultPosition]=result;
    						printf("\nmultDivide positions:%i\n",multDivideResultPosition);
    					}			
    				
    
    			} // close of if(operations * or / ) bracket
    			
    				// Stop counter of multDivide result position in final array from incrementing willy nilly
    				if((multDivide !=true)||(countStop<=1))
    					{
    						// answersArray[multDivideResultPosition]=result;					
    						multDivideResultPosition++;
    					}
    				i++;
    
    		}while(operations[i]!='\0');
    
    		getchar(); 
    
    getchar();
    		
    		
    }
    In the workstring it is yanking and converting every digit except the last 19 and I am having trouble seeing why that is.

    Beyond that I think I almost got it. This project is pushing the limit of what I can hold in my head at once.

    The last person who replied talking about tokenizing them, I have no idea what he is trying to say. Went right over my head.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. help with calculator program.
    By sadr in forum C Programming
    Replies: 9
    Last Post: 09-10-2009, 10:30 AM
  2. I need help with this Calculator Program ..
    By Kawaii JoJo in forum C++ Programming
    Replies: 4
    Last Post: 05-14-2009, 03:06 PM
  3. First Program (Calculator)
    By bjl in forum C++ Programming
    Replies: 15
    Last Post: 02-03-2008, 04:13 PM
  4. Calculator program
    By treenef in forum C++ Programming
    Replies: 5
    Last Post: 11-23-2005, 03:10 AM
  5. Help with calculator program.
    By banjordan in forum C++ Programming
    Replies: 1
    Last Post: 12-03-2003, 10:01 PM