Algebraic calculator algorithm - Page 1
Forum section: electronics / general

author: TheWinterSnow
author_rank: Contributor
timestamp: June 04, 2015, 01:32:10 am
content: Not so much hardware related but related to an AVR project that I am working on.I have been in the process of using an ATMega2560 to make a physically functioning programmable scientific calculator. Last semester in my programming class we built an RPN calculator in java and my professor made mention that an algebraic calculator would be difficult to make, I scoffed at that until just yesterday.The basic operation of a scientific calculator is extremely complicated...and as I have discovered very convoluted.There are a couple of rules I have to abide by:- I am using a stack to hold my numbers and math functions- The stack should be kept as small as possible and only parenthesis require more than two items on the stack- math functions such as + - * / should act as an = but remembers to do math to the next new numberThe last one is what makes things confusing, because I am trying to make a calculator that operates like a standard scientific calculator or just a standard calculator when you type 1 + 2 and then press + again, it computes the previous equation to 3 the instant you hit the second +, but remembers that it is adding the following number about to be input. Keep in mind, the input is being done with a command terminal style setup where the code tests for the most recent input.The problem is because I am using a command line based system, I need to know what has happened previously, and I am in a rut that looks like in order to do so I have to use a whole gambit of flags and complicated logic that seems unnecessary.this is what I have so far, minus the functions in other files that should be self explanatory:Code:int main(){char userInput;double data = 0;while(1){// TODO Initialize LCD// TODO while loop scans for button pressed// sets port value to variable// get initial user inputprintf(">: ");scanf("%s", userInput);// First itteration, first input must be a number// Scans until a valid number is givenwhile ((double)userInput < 48 || (double)userInput > 57){printf(">: ");scanf("%s", userInput);}// Enters 1 number into the data variablenumberEntry(userInput, &data);printf("\n %f", data);// TODO while loop that polls until button depress// scan for number or commandprintf("\n>: ");scanf("%s", userInput);// continue loop as long as userInput is a numberwhile((double)userInput >= 48 && (double)userInput <= 57){numberEntry(userInput, &data);printf(" %f", data);printf("\n>: ");scanf("%s", userInput);}push(data);data = 0;// ---- Break out of inputing numbers to do math// addif(strcmp(userInput, "+") == 0){push(pop() + data);data = 0;printf(" %f\n", peek());}// subtractelse if(strcmp(userInput, "-") == 0){push(pop() - data);data = 0;printf(" %f\n", peek());}// multiplyelse if(strcmp(userInput, "*") == 0){push(pop() * data);data = 0;printf(" %f\n", peek());}// divideelse if(strcmp(userInput, "/") == 0){push(pop() / data);printf(" %f\n", peek());}else if(strcmp(userInput, "clr") == 0){first = null;data = 0;printf(" %f\n", peek());}}return 0;}obviously this code doesn't work, it pulls a zeros from the stack and adds that. I am at a loss here, if anyone could fill me in for some better topologies that would be great.I am looking into making the basic calculator and the hardware open source so that people that build their own scientific calculator, getting the input to work like a classical sceintific calculator would be the biggest breakthrough o f the project.BIG NOTE: If it hasn't seemed obvious I am trying to code in C language for PC just to get a working prototype and once I works will modify the code to work for embedded systems, hence why you see stdio.h and stdlib.h functions and console displays.

author: rs20
author_rank: Super Contributor
timestamp: June 04, 2015, 02:41:19 am
content: So one immediate point. RewriteCode:while ((double)userInput < 48 || (double)userInput > 57){AsCode:while (userInput < '0' || userInput > '9'){No need to cast it to a double, nor cryptically use ASCII codes.But the main thing you need to do is to define what behaviour you actually want, because I'm not familiar with the sort of calculator your talking about. Not just so that we know what you want, but the act of specifying the behaviour you want in a precise and systematic way is half the task of coding it even if you're doing it by yourself. "Just a standard calculator" is horrendously vague.So what is the screen supposed to say after I've typed "5*7+3*"? And what is it supposed to say after I've typed "5*7+3*2="? 41 or 76? Your standard 4-banger will say 76, an S-VPAM will say 41.

author: IanB
author_rank: Super Contributor
timestamp: June 04, 2015, 02:55:20 am
content: Traditional scientific calculators that respond immediately to key presses have an X and a Y register. The X register is hidden and the Y register appears on the display. When you enter a value it is shifted into the Y register. If you press a binary operator key like + or * the value in the Y register is copied into the X register, the operation is stored, and the Y register is set to accept a new entry. If you then press another binary operator key then the X and Y registers are combined using the stored pending operator, the result is placed in the X register, the key press is stored as a new pending operator, and the Y register is reset to accept a new entry. There are variations around this for the = key (a no-op), for example.If the calculator handles arithmetic precedence then the X register becomes a number stack and the pending operation register also becomes a stack. Pushing or unwinding the stack now depends on the relative precedence of the key presses and what is on the stack.(Unary operators such as sin or sqrt simply replace the contents of the Y register with the result, leaving the X register and the stack untouched.)

author: rs20
author_rank: Super Contributor
timestamp: June 04, 2015, 03:01:37 am
content: Yes, assuming the OP wants the "Immediate execution" Calculator Input Method, IanB's description is right. Of course, even with immediate execution, you need a larger stack to handle brackets, and the OP clearly had this in mind with "The stack should be kept as small as possible and only parenthesis require more than two items on the stack". So, i.e, only 2 items on the stack.TheWinterSnow: I think you're getting yourself tangled up dealing with the special case of the first input number. I mean, clearly your code is trying to consume two numbers before consuming the first math operator. Obviously the math operator should be consumed between the two numbers. If I were you, I would make it so that the variables were initialized as if you're halfway through a formula, and get it to work correctly starting from that point.Thenadd your initial special case. Start by writing pseudocode if you think that'll help. But youhaveto read in the math operator before you read in the second number.

author: atferrari
author_rank: Frequent Contributor
timestamp: June 04, 2015, 03:07:10 pm
content: Wouldn't a flow diagram be of help here?I use it always, because, first of all, it helps me to have a clear idea of what the algorithm should be.Then, writing code seems more a clerical task.

author: amyk
author_rank: Super Contributor
timestamp: June 04, 2015, 03:29:19 pm
content: If you ignore precedence and parens and just do everything sequentially then the algorithm of which IanB has explained will suffice; but if you want to handle parentheses and have the correct precedences, then you'll need some algorithm like this:https://www.engr.mun.ca/~theo/Misc/exp_parsing.htmHere is a very minimal implementation of this technique; it's a C compiler but contains the expression parser that's relevant:https://github.com/rswier/c4/blob/master/c4.cIf you happen to know x86 asm, this may also interest you:http://www.hugi.scene.org/compo/compoold.htm#compo4

author: IanB
author_rank: Super Contributor
timestamp: June 04, 2015, 04:28:13 pm
content: Recursive descent parsing does indeed work very well for "algebraic entry" calculators, but the alternative, stack-based approach that I outlined is possibly more convenient for "immediate response" calculators.I implemented such an algorithm based on a reference that I read many years ago. It would take me more time than I have available now to describe the algorithm in detail, but maybe I can summarize it later. It will handle precedence ordering, parentheses and mathematical functions in the expected way and is not limited to pure sequential evaluation.

author: jolshefsky
author_rank: Regular Contributor
timestamp: June 04, 2015, 05:10:26 pm
content: Let me make use of my computer science degree minted in 1993 ...I would take a look at the problem from a binary tree perspective and work from there to make optimizations. I would also keep in mind what to do with erroneous input (e.g. "3 * - 5": does that mean "multiply-I-meant-subtract" or "multiply by minus 5"?)For the binary tree, you can represent all the two-parameter operations (add, divide, etc. versus cosine or square root) in it where any node can either be a number or an operator that has two children. You can then traverse the tree to generate prefix, infix, or postfix notation (where you output a node when it is first visited, between its children, or after all its children have been visited.) For instance:3 * 4 + 5 * 8 would make a tree like:Code:+* *3 4 5 8Traversing postfix yields the children first, so you'd get 3 4 * 5 8 * + and you're back to the easy RPN functionality.The trick, so-to-speak, is to collapse the tree as soon as you can get an answer. So 3 * 4 + can immediately be collapsed to (the equivalent) 12 +. "12 + 5 *" is tricky because you need a tree portion that is the whole expression because you need to wait for 5 * 8 until you can get 40 then 12 + 40 = 52. Parentheses just force the tree into an order-of-operations that is non-natural (e.g. "( 12 + 5 ) * 8" builds a different tree, but the closing parenthesis collapses "12+5").In conclusion, let me say that infix notation is stupid and ambiguous and I hate it.

author: miguelvp
author_rank: Super Contributor
timestamp: June 04, 2015, 06:40:48 pm
content: I made an in program compiler that would translate an algebraic formula into 8087 machine language. Pretty much doing the same thing as jolshefsky suggests, I would build the tree while interpreting the equation so in effect converting it to RPN, which suited the math coprocessor.I'll check if I still have that code, but it's old (30 years ago) but the ending program still works in dos box. I'll put a screenshot if I can run it here at work.My brother did the program I only did the compiler but it made it way way faster since he was interpreting the formula per iteration.

author: senso
author_rank: Frequent Contributor
timestamp: June 04, 2015, 06:45:19 pm
content: Take a look on this PIC32 based calculator:http://www.gabotronics.com/product-info/design-coma.htm

author: miguelvp
author_rank: Super Contributor
timestamp: June 04, 2015, 07:28:33 pm
content: Found the executable and it works. I'll check if I can find the code, CGA graphics, did support Hercules and maybe EGA not sure.Take into account this is 30 years old and the code is really not that well written.

author: IanB
author_rank: Super Contributor
timestamp: June 04, 2015, 08:18:21 pm
content: Quote from: jolshefsky on June 04, 2015, 05:10:26 pmHere's an example of how to do stack based evaluation in immediate mode as with old style calculators.Given the expression "3 * 4 + 5 * 8 =", the evolution of the stacks goes like this:Enter: 3Number stack: 3Operator stack: (empty)Enter: *Number stack: 3Operator stack: *Enter: 4Number stack: 4 3Operator stack: *Enter: +Number stack: 12Operator stack: +Note: + has lower precedence than * so it forces the existing operator stack to be evaluated before + is pushed on=> pop *, pop 4, pop 3, evaluate 3 * 4, push 12=> push +Enter: 5Number stack: 5 12Operator stack: +Enter: *Number stack: 5 12Operator stack: * +Note: * has a higher precedence than + so it gets pushed on the stack.Enter: 8Number stack: 8 5 12Operator stack: * +Enter: =Number stack: 52Operator stack: (empty)Note: entering = causes the whole operator stack to be evaluated=> pop *, pop 8, pop 5, evaluate 5 * 8, push 40=> pop +, pop 40, pop 12, evaluate 12 + 40, push 52----An expression with parentheses, for example "5 * (4 + 3) - 1 =":Enter: 5Number stack: 5Operator stack: (empty)Enter: *Number stack: 5Operator stack: *Enter: (Number stack: 5Operator stack: ( *Enter: 4Number stack: 4 5Operator stack: ( *Enter: +Number stack: 4 5Operator stack: + ( *Enter: 3Number stack: 3 4 5Operator stack: + ( *Enter: )Number stack: 7 5Operator stack: *Note: a closing parenthesis causes the operator stack to be unwound down to the matching open parenthesis.=> pop +, pop 3, pop 4, evaluate 4 + 3, push 7=> pop (, discard itEnter: -Number stack: 35Operator stack: -Enter: 1Number stack: 1 35Operator stack: -Enter: =Number stack: 34Operator stack: (empty)

author: TheWinterSnow
author_rank: Contributor
timestamp: June 05, 2015, 12:33:22 am
content: Quote from: IanB on June 04, 2015, 08:18:21 pmI got your first post down and got a basic + - * / program working but now this one quoted has me scratching my head a little bit. I don't want to get convoluted with unnecessary logic so I will just ask, what it the most simplistic way to determine order of precedence when are just loading ASCII values '+' '-' '*' '/' '(' ')'? In my final iteration of the code when it gets embedded to the hardware format I planned on using integers mapped out to specific math functions but even then what is the determining factor on precedence considering that it changes when you are using parenthesis?I am not asking to write the code for me, but rather, how do you go about setting yourself up for the task. At this point this project is over my head and as a result I am trying to come up with more complicated solutions than are needed.

author: IanB
author_rank: Super Contributor
timestamp: June 05, 2015, 12:51:17 am
content: Precedence can be coded as an integer value.Something like this:* 2/ 2+ 1- 1( 0Then you just compare integer values using greater than or less than.If any new operator has lower or equal precedence than the operator on top of the stack it forces the stack to be evaluated until it is empty or until a '(' is reached.'(' gets a precedence of 0 since any binary operator will get stacked on top of it.If you include mathematical operators like 'sin' or 'sqrt' they operate immediately on the number at the top of the number stack and cause that value to be replaced with the result.Also note that calculators have a '+/-' key for entering negative values so there is no confusion with the '-' key for subtraction.

author: IanB
author_rank: Super Contributor
timestamp: June 05, 2015, 01:04:39 am
content: Quote from: TheWinterSnow on June 05, 2015, 12:33:22 amActually, precedence doesn't change when using parentheses. Parentheses merely have the effect of grouping certain operations together so they happen in a different order.

author: TheWinterSnow
author_rank: Contributor
timestamp: June 05, 2015, 02:12:57 am
content: Quote from: IanB on June 05, 2015, 01:04:39 amThat is what I meant, that if you have z * (x + y), the x+y comes first, as oppossed to z * x + y where z * x will be executed first, if the precendence of x + y is lower by some integer value, how do you go about overriding the fact that you have a * in the stack?

author: IanB
author_rank: Super Contributor
timestamp: June 05, 2015, 02:25:01 am
content: Quote from: TheWinterSnow on June 05, 2015, 02:12:57 amI showed you in my second example; read it carefully.

author: legacy
author_rank: Super Contributor
timestamp: June 05, 2015, 11:50:58 am
content: inthistopic I have linked a toy-project I did. It contains an interpreter plus a little calculator, and it's C and C++ source. Feel free to study, and to improve, and … let me know any progress (if you want). Actually that project is in its dead-line, I am coding a new interpreter with a completely different approach.

author: TheWinterSnow
author_rank: Contributor
timestamp: June 06, 2015, 12:23:06 am
content: Quote from: IanB on June 05, 2015, 02:25:01 amThanks, I really have fried my brain on this project, I was confusing your first post with your second. I think its time a step away from this project for a bout a week or so.Quote from: legacy on June 05, 2015, 11:50:58 amAn compiler seems like to be the best option in the long run. I think for now I am just going to go without functions and have very simple predefined equations on a simple stack based interpreter mentioned over on the first page.I would like to learn Lex and Yacc, JavaCC, and AST but that is so over my head at the moment. At the moment I am just using an ATMega 2560 to replicate my TI-36X that I can add extra functions as needed. Once I gain the computing knowledge to add more functions, I will move to an ARM solution with mentioned compiler setup when I can get my head around said concepts.

author: mushroom
author_rank: Regular Contributor
timestamp: June 07, 2015, 02:03:27 am
content: Not sure it will fit your needs, but you could consider this :http://www.codeproject.com/Articles/6877/Simple-x-Numerical-Formula-ParserThe sources are easy to read/understand/port. There are some bugs I reported to the author, but I don't know if they where fixed in the sources. Just go down to "Comments and Discussions", you will find the fixes.

author: ale500
author_rank: Frequent Contributor
timestamp: June 07, 2015, 01:04:52 pm
content: Parsing algebraic expression can be accomplished several ways. It all depend on what do you want to do with the parsed data. If you are just evaluating numbers, a recursive algorithm is probably the simplest of the bunch. Immediate execution is also a good idea, and shouldn't be much more difficult. We learned immediate execution at school, in math classes, I mean. it is kind of intuitive, I guess.I am after a similar goal and decided, long ago, that a binary tree would be the best because I'd like a grapher and a solver too.If you want some advice regarding the embedded development, I'd suggest you build a library you can compile and test on your pc (Unit Tests save the day, you can see where you broke something...). When that works, go for a uC. Use static data, avoid printf (except for debug) and dynamic memory altogether and you will be mostly on the safe side.One note: x86, Cortex-Mx allow for unaligned accesses, MIPS (PIC32) doesn't ! (learned the hard way a while ago...). Aligned accesses are faster anyways so try to use them.Edit: I forgot another thing you have to do without (*): floating point !, specially double.* AVR had only support for float, it is emulated anyways. The Cortex-M4 has built-in float support. There are emulation libraries, you may want to look at gcc, the sources, it had double support, code to emulate I mean.If you want to go for one of the best alternatives, I'd suggest you look at bc. It is a unix command, it implements arbitrary-precision, like the TI-92.IBM produced something called decNumber, it is a library, sadly it uses malloc, but it may help you. It is what is being used for the WP-34, something you definitely may want to look at.

author: ale500
author_rank: Frequent Contributor
timestamp: June 09, 2015, 08:04:26 am
content: I know it is not C and it may have some bugs, but it seems to work. An immediate execution (kind of) algebraic to postfix AKA RPNCode:#!/usr/bin/python# simple formula parser# dictionary for the precedenceprecedence = dict( [('+', 5), ('-', 5), ('*', 6), ('/', 6)] )stack = []def push(arg):if arg != ' ': # spacers represent argument that are already on the stackstack.append(arg)def isNumber(tok):try:n = int(tok)except:return Falsereturn Truedef isOp(tok):if tok in '+-*/':return Truereturn False# searches to the right of the current position for an operand with higher precedence# it stops if finds a parenthesis or end## 3 + 5 * 2 should give *# 3 + 6 + 7 should give the first +#def searchToTheRight(formula, start, end):best = -1 # nothing foundfor i in range(start, end):if isOp(formula):if best != -1:if precedence[formula] > precedence[formula]:best = ielse:best = ireturn best# use a list of arguments to simplify# possible parenthesisdef isParenOrFunc(tok):return tok in [ '(', 'sin(', 'cos(', 'tan(' ]def isFunc(tok):return tok in [ 'sin(', 'cos(', 'tan(' ]def parseFormula(formula):left = Noneright = Noneoperator = Nonelformula = formula # it is going to be reducedfinished = Falsewhile finished == False:print 'Parsing: ' + str(lformula)size = len(lformula)# search for right-most '('lparen = -1 # no parenthesis or function foundrparen = -1for idx in range(size):if isParenOrFunc(lformula):lparen = idxelif lformula == ')':# we found it...rparen = idxbreakif lparen == -1:start = 0func = Noneelse:start = lparen + 1func = lformulaif rparen == -1:end = size - 1else:end = rparen - 1if lparen != -1 and rparen == -1:print "Error: mismatched () pairs"quit()if start > end: #finished = Trueprint "Finished: Empty expression"breakif start - end == 0: # one number onlyprint 'start - end = 0'if lformula == ' ':finished = Trueprint "Finished: Empty expression"breakpush(lformula) # push numberif isFunc(func):push(lformula) # push to stacklformula = ' ' # spacerprint lformula, lparen, rparenif rparen != -1:del lformulaif lparen != -1:del lformulaelse:# search for operand with highest precedenceprint 'start - end > 0'op = searchToTheRight(lformula, start, end)if op == -1:print 'Error: no operand'quit()if op == start:print 'Error: no left operand'quit()if op == end:print 'Error: no right operand'quit()push(lformula[op - 1])push(lformula[op + 1])push(lformula) # operandif (start - 1 == lparen) and (end + 1 == rparen): # there was only one expressionif isFunc(func):push(func) # push to stackdel lformula # rparendel lformula[op + 1]del lformuladel lformula[op - 1]lformula = ' 'else:del lformula[op + 1]del lformulalformula[op - 1] = ' '# TestsparseFormula([ '3' ])print 'Stack ' + str(stack) + '\n'stack = []parseFormula([ '3', '+', '4' ])print 'Stack ' + str(stack) + '\n'stack = []parseFormula([ '3', '+', '4', '*', '5' ])print 'Stack ' + str(stack) + '\n'stack = []parseFormula([ '(', '3', '+', '4', ')'])print 'Stack ' + str(stack) + '\n'stack = []parseFormula([ '4', '*', '(', '3', '+', '4', ')'])print 'Stack ' + str(stack) + '\n'stack = []parseFormula([ '4', '*', '(', '3', '+', '4', ')', '*', '6'])print 'Stack ' + str(stack) + '\n'stack = []parseFormula([ 'sin(', '3', '+', '4', ')', '*', '6'])print 'Stack ' + str(stack) + '\n'stack = []Code:Parsing: ['3']start - end = 0[' '] -1 -1Parsing: [' ']start - end = 0Finished: Empty expressionStack ['3']Parsing: ['3', '+', '4']start - end > 0Parsing: [' ']start - end = 0Finished: Empty expressionStack ['3', '4', '+']Parsing: ['3', '+', '4', '*', '5']start - end > 0Parsing: ['3', '+', ' ']start - end > 0Parsing: [' ']start - end = 0Finished: Empty expressionStack ['4', '5', '*', '3', '+']Parsing: ['(', '3', '+', '4', ')']start - end > 0Parsing: [' ']start - end = 0Finished: Empty expressionStack ['3', '4', '+']Parsing: ['4', '*', '(', '3', '+', '4', ')']start - end > 0Parsing: ['4', '*', ' ']start - end > 0Parsing: [' ']start - end = 0Finished: Empty expressionStack ['3', '4', '+', '4', '*']Parsing: ['4', '*', '(', '3', '+', '4', ')', '*', '6']start - end > 0Parsing: ['4', '*', ' ', '*', '6']start - end > 0Parsing: [' ', '*', '6']start - end > 0Parsing: [' ']start - end = 0Finished: Empty expressionStack ['3', '4', '+', '4', '*', '6', '*']Parsing: ['sin(', '3', '+', '4', ')', '*', '6']start - end > 0Parsing: [' ', '*', '6']start - end > 0Parsing: [' ']start - end = 0Finished: Empty expressionStack ['3', '4', '+', 'sin(', '6', '*']

author: TheWinterSnow
author_rank: Contributor
timestamp: August 08, 2015, 12:13:50 am
content: Quote from: IanB on June 05, 2015, 12:51:17 amThis has me a little confused here. If I already have a multiplication on the top of the operator stack and I input an addition, the operator stack will execute the multiplication (Ex: 5 * 3 + gets executed to 15 +). But now you say that '(' gets a precedence of 0 so 5 * ( would cause the stack to evaluate the multiplication since it has higher precedence than the '('.Anyway I managed to get it working without the parentheses needing precedence. I haven't added unary operators yet like sqrt() and trig functions but the hardest part is behind me.Here is the rough code, I know it probably horribly inefficient. I temporarily moved over to java as I thought it would be easier to work out the function of the program. I have a feeling it's pretty crusty so any recommendations or pointers would be great.Code:while(true){while(!input.equals("=")){System.out.print("> ");input = getInput();// get number// if input is "(" instead of numberif(input.equals("(")){// continue op.push() for additional "("while(input.equals("(")){op.push(input);// push operator to op stackSystem.out.print("> ");input = getInput();}num.push(input);}// input is a numberelse {num.push(input);// push number to num stack}System.out.print("> ");input = getInput();// get operatorif(!input.equals("=")){if(input.equals(")")){while(input.equals(")")){//unwind the stack until the next "("while(!op.peek().equals("(")){num.push(doMath());}op.pop();// pops ooff the finished "("System.out.print("> ");input = getInput();}}else{precedence = getPrec(input);// precedence == newest operatorif(precedence <= getPrec(op.peek())){// getPrec(op.peek()) == older operator// do the operation currently in the stacknum.push(doMath());op.push(input);}else {// push the new operator onto the stackop.push(input);// push operator to op stack}}}}// ties up loose endswhile(!op.isEmpty()){num.push(doMath());}System.out.println("> Final Result: " + num.pop());input = "";}

author: IanB
author_rank: Super Contributor
timestamp: August 08, 2015, 04:24:43 am
content: Quote from: TheWinterSnow on August 08, 2015, 12:13:50 amWhen '(' is encountered it is always pushed onto the operator stack. It never triggers evaluation itself.

author: TheWinterSnow
author_rank: Contributor
timestamp: August 08, 2015, 04:27:36 am
content: That is what I did in my code.

author: legacy
author_rank: Super Contributor
timestamp: August 08, 2015, 11:10:35 am
content: I have improved my code, and I have chosen the RPN way, definitivelyCode:# eval 1+2+3yards analysis: PASSEDstack analysis: PASSEDfunction_pool_show_all ... 0 functionsrpn_v[]={ 0 2 1 4 3 } 1 K3 cookie token_DecValue, type12 1 K3 cookie token_DecValue, type12[+] -1 K5 operator token_Plus, type53 1 K3 cookie token_DecValue, type12[+] -1 K5 operator token_Plus, type53########################## good expression ##########################Code:# eval 1+Fa(2)+Fb(3+4)yards analysis: PASSEDstack analysis: PASSEDfunction_pool_show_all ... 2 functions+ function1  { type12 } 1 args ticket_rpn=2+ function2  { type12 } 1 args ticket_rpn=7rpn_v[]={ 0 4 2 1 9 11 10 7 6 } 1 K3 cookie token_DecValue, type12 1 K4 f_cookie token_DecValue, type12 0 K2 function token_StrictAlphaNum, type19[+] -1 K5 operator token_Plus, type53 1 K4 f_cookie token_DecValue, type12 1 K4 f_cookie token_DecValue, type12[+] -1 K6 f_operator token_Plus, type53 0 K2 function token_StrictAlphaNum, type19[+] -1 K5 operator token_Plus, type53########################## good expression ##########################Code:# eval 1+(2.2*3.0E9+ ( 0xdeadbeaf logicalAnd 0xffffffff ) )yards analysis: PASSEDstack analysis: PASSEDrpn_v[]={ 0 3 5 4 8 10 9 6 1 } 1 token_DecValue, type12[2.2] 1 token_FloatinPointENGValue, type15[3.0E9] 1 token_FloatinPointSCIValue, type16[*] -1 token_Asterisk, type55 1 token_HexValue, type13 1 token_HexValue, type13 -1 token_LogicalAnd, type41[+] -1 token_Plus, type53[+] -1 token_Plus, type53########################## good expression ##########################(ops, type check is needed)

author: legacy
author_rank: Super Contributor
timestamp: August 08, 2015, 06:40:18 pm
content: it works over a lexerthe input string (from a shell) is divided into tokens, tokens enter into a yards algorithm, the result is a vector, plus a booleanthe vector tells the order of the token on the stack (RPN notation)the boolean tells if the expression is valid or notfunctions are allowed, but ... to support them the complexity has grown of a factor of 3

author: djacobow
author_rank: Super Contributor
timestamp: August 10, 2015, 03:31:26 pm
content: Miguelvp's program brings a tear to this oldster's eye. When computers were computers.

author: TheWinterSnow
author_rank: Contributor
timestamp: August 11, 2015, 03:56:24 am
content: Quote from: ale500 on June 07, 2015, 01:04:52 pmSee this is the problem, since I am using more advanced libraries to do trig and other functions, there is no way I am writing my own math functions, not going to happen. The execution times of FP is completely ok by me as even some of the worst math functions can still be executed in less than 1ms. MMy problem right now if trying to find out how to convert FP numbers with their errors (or not in most cases) back to integer fractions. Converting 1.333333333333333 back into 4/3 is something I am currently blanking on.

author: ale500
author_rank: Frequent Contributor
timestamp: August 11, 2015, 04:20:08 am
content: The math lib used is just a part of the calculator. Rolling your own is hard because you have to develop one algorithm for every function, rounding and so on. One way to avoid some errors is to use decimal arithmetic instead of binary. Another is to use symbolic math all the time:Say the user want to do (1 + 2) / 4 and wants 3/4 as an answer...If you say keep the history of input you get 3 / 4 directly. The problem comes with 0.75 as the result of sin(48.59°). In this case you can only approximate. The TI-92 for instance uses decimal throughout and 64 bit float for transcendental functions, and not only decimal but rational numbers, numbers stored as fractions (to avoid unnecessary divisions).

author: DJohn
author_rank: Regular Contributor
timestamp: August 11, 2015, 02:06:28 pm
content: Quote from: TheWinterSnow on August 11, 2015, 03:56:24 amContinued fractions are probably your friend here. You'll never be able to convert exactly (because 4/3 is not representable in floating point), so the best you can do is generate a sequence of rational approximations and pick one that still involves reasonable numbers.Here's an example: say we want to represent 5.123456789 as a fraction. Remove the integer part, giving us our first approximation of 5, with remainder 0.123456789. Take the reciprocal of the remainder, 8.10000007371. The integer part gives out next approximation, 5 + 1/8, and the remainder is 0.10000007371. Take the reciprocal again, which is 9.99999262901. The integer part gives the next approximation, 5 + 1/(8+1/9) = 5 + 9/73. The remainder is now 0.99999262901. Taking the reciprocal of this gives 1.00000737104, so out next approximation is 5 + 1/(8+1/(9+1/1)) = 5 + 10/81. The remainder is 0.00000737104, which is pretty small number. We're not going to gain much accuracy by continuing, so I'll choose to stop there.5 + 10/81 = 5.12345679012, which is not bad.The process is basically saying "The number is 5. Well, not really 5, but 5 + 1/8. Well, not really 8, but 8 + 1/9..." and so on.

author: TheWinterSnow
author_rank: Contributor
timestamp: August 12, 2015, 03:03:12 am
content: Quote from: DJohn on August 11, 2015, 02:06:28 pmThanks that is amazing, after a bit of messing around on some of my TI calculators I get what is going on here. My TI-36X can convert decimal to fractions to some degree, on the one I am designing I will have a flag that denotes whether the answer is exact or an approximate so it will always convert to a fraction if asked to do so.I didn't work on the fraction stuff today as I wasn't mentally ready to try and figure out how to implement Least Common Multiples to add and subtract fractions, as well as the recursive in nature algorithm posted above. I will get around to it though. Instead I worked on getting the conversion of decimals to exact value irrational numbers. So far it is working, just need to integrate the exact values into the main calculator.That leaves me wondering though, since I have no experience in even using graphing calculators, are there calculators that do compute exact values for some irrational numbers?

author: ale500
author_rank: Frequent Contributor
timestamp: August 12, 2015, 04:54:57 am
content: An irrational number cannot be obtained by the division of two whole numbers. You can approximate, but that is not exact, at least for the definition of exact that I know of.

author: helius
author_rank: Super Contributor
timestamp: August 12, 2015, 05:32:30 am
content: Irrational numbers can, of course, be represented symbolically, which is "exact".There are several different types, the most familiar being algebraic (a rational raised to a rational power). I think most calculators support that to some degree.Transcendentals are the non-algebraic type, but some can be calculated in other ways, like Maclaurin series.There are also some reals that are not computable. So they must be transcendental irrationals, but there is just no way to find them.

author: TheWinterSnow
author_rank: Contributor
timestamp: August 13, 2015, 12:54:57 am
content: The way I got the decimal approximations back into their exact form was to compare a preset table of approximates to the input, if they are equal to the tolerance that some of the various math functions have (ex, cos() has one less digit than sqrt()) then the input is stored back into a symbolic form. It works for squares whose values are above sqrt(2), but not less, however, I already have an exact table for trig functions, so it comes down to just integrating the two tables together. I still have to do tables for nth-root values depending on how many tables I want to have before the calculator gives up and just keeps the approximate number.

author: legacy
author_rank: Super Contributor
timestamp: August 13, 2015, 01:11:22 am
content: how big (byte size) are these tables ?

author: helius
author_rank: Super Contributor
timestamp: August 13, 2015, 01:34:22 am
content: I'm sure that for most use cases, this comparison process nullifies the speed advantage of using floating point in the first place.

author: TheWinterSnow
author_rank: Contributor
timestamp: August 13, 2015, 02:04:10 am
content: Quote from: legacy on August 13, 2015, 01:11:22 amRight now, 208 Bytes if an int == 16-bits. I am thinking at the moment to scrap the lookup tables, modify my stack and keep everything in exact form symbolically until I am ready for an approximate answer. But I am still thinking I need a lookup table of some sort to get exact answers of common trig functions. Ex: cos(PI/4), there needs to be a table to spit out sqrt(2)/2 symbolically and put that into the stack. I don't need a table for sqrt() or nth-rt() functions because they can just be symbolically stored in the stack until it is time to evaluate for the final answer....I think.Quote from: helius on August 13, 2015, 01:34:22 amI am not worried too much about speed, so long as the final product isn't any slower than my TI-36X, which takes about 5 seconds to process !n(65). Again being able to spit out math functions within 200-300uS worst case is not a concern.

author: legacy
author_rank: Super Contributor
timestamp: August 13, 2015, 06:36:01 pm
content: seethispage, funny code inside

author: TheWinterSnow
author_rank: Contributor
timestamp: August 18, 2015, 06:38:06 am
content: So this project started to get a little complicated. The more and more I thought about features the more is slowly starting turning into a CAS like system. At first I thought that would be easy until I really thought about it, then decided it was too complicated and just going back to the original standard scientific calculator would be best for my sanity.Since writing CAS capable code is out of the question, I think it would benefit me to get my hands on an existing CAS library and use that to extend my project as needed. Problem is, I can't seem to find such a library for embedded systems. I am having the odd feeling such library wouldn't fit onto an AVR 8-bit solution, but I have been debating moving over to a Rasperry Pi setup. Under both circumstances, what CAS C++ libraries out there would work with AVR or RP/Raspbian?GiNaC is more of a Unix setup, with distributions for Debian, but I am not too familiar with Unix-Like OS and have no clue if it could work with an OS like Raspbian.

author: legacy
author_rank: Super Contributor
timestamp: August 19, 2015, 12:12:16 am
content: some time ago we openedthis(sw) andthis(hw) threads (italian language), in order to realize something similar: RPI linux with a calc engine.Currently the project is stalledI have to check my friend answers, my be he has already found a good open source software for CAS under linux

author: timofonic
author_rank: Frequent Contributor
timestamp: August 19, 2015, 03:24:12 am
content: I'm a totally n00b, but maybe this could inspire you...http://sourceforge.net/projects/wp34s/