CommanderKeith
|
 |
«
Posted
2009-03-14 12:39:29 » |
|
Hi, Does anybody know of a mathematical expression evaluator that can calculate expressions like "2(1.05^(1/5))"? So it needs to be able to do Math.pow(x, y) using ^ and it would also be cool if it could do implicit multiplication (like 2(1+3)). It's for a commercial project. I've been searching but can only find commercial API's like JEP and JFormula. There must be a free one out there...  Thanks a lot, keith
|
|
|
|
pjt33
|
 |
«
Reply #1 - Posted
2009-03-14 13:08:22 » |
|
If you're using Java 1.6 then can't you use the built-in JavaScript implementation?
|
|
|
|
|
|
|
Games published by our own members! Check 'em out!
|
|
cylab
|
 |
«
Reply #3 - Posted
2009-03-14 18:33:53 » |
|
^ is the Xor operator in java and javascript (and possibly other languages too)
|
Mathias - I Know What [you] Did Last Summer!
|
|
|
CommanderKeith
|
 |
«
Reply #4 - Posted
2009-03-15 02:41:55 » |
|
Ah ok, that explains it! thanks
|
|
|
|
|
|
irreversible_kev
|
 |
«
Reply #6 - Posted
2009-03-15 21:27:52 » |
|
to see if you can break it,
1: 1-1-1 2: (1)(1) 3: (1)^(1/2) 4: +1 5: (1/3)3 <- not really sure if that is valid input It seems fairly intelligent otherwise 
|
|
|
|
|
woogley
|
 |
«
Reply #7 - Posted
2009-03-15 22:07:54 » |
|
1: 1-1-1 2: (1)(1) 3: (1)^(1/2) 4: +1 5: (1/3)3 <- not really sure if that is valid input
Thanks! Nothing like a good ego killer  Just kidding.. I appreciate you posting the bugs (and identifying that invalid input). All of the above have been fixed Please let me know if you find any more bugs
|
|
|
|
|
Riven
|
 |
«
Reply #8 - Posted
2009-03-15 22:22:27 » |
|
My parser handles this, how about yours:
-1+-2-3 = -6 -1--2-3 = -2
And in that code I don't see any operator precedence.. ?
|
|
|
|
woogley
|
 |
«
Reply #9 - Posted
2009-03-15 22:26:59 » |
|
My parser handles this, how about yours:
-1+-2-3 = -6 -1--2-3 = -2
And in that code I don't see any operator precedence.. ?
Did you try those expressions with the Test jar? My parser handles both correctly, so I don't see your point? Also, from the code: 1 2 3 4
| for (j = 0;j < OPERATORS.length();j++) { |
|
|
|
|
|
Games published by our own members! Check 'em out!
|
|
Riven
|
 |
«
Reply #10 - Posted
2009-03-15 22:34:15 » |
|
Hm... my bad..
anyway this one fails to evaluate:
-(-.1)
|
|
|
|
woogley
|
 |
«
Reply #11 - Posted
2009-03-15 22:41:32 » |
|
anyway this one fails to evaluate:
-(-.1)
Fixed - thanks
|
|
|
|
|
Riven
|
 |
«
Reply #12 - Posted
2009-03-15 22:41:49 » |
|
((12)+1)+1 ((12))+1
Seems like the parsers barks on "(("
A quick hack could be to replace all "((" by "(1("
It also crashes on "))"
|
|
|
|
Riven
|
 |
«
Reply #13 - Posted
2009-03-15 22:50:19 » |
|
"1-3+4" evaluates to -6 ?! as in (1-(3+4))
Operator precendence: - and + are on the same level, just like * and /
|
|
|
|
woogley
|
 |
«
Reply #14 - Posted
2009-03-15 23:13:14 » |
|
The parentheses thing was just being caused by using lastIndexOf (which obviously won't cover mutltiple same-level groups) "1-3+4" evaluates to -6 ?! as in (1-(3+4))
Operator precendence: - and + are on the same level, just like * and /
Ah yes, this is an important bug, glad you pointed it out. The program was just using the "Please excuse my dear aunt sally" approach without accounting for same-level ops All bugs above have been resolved Thanks, -- Matt
|
|
|
|
|
Riven
|
 |
«
Reply #15 - Posted
2009-03-15 23:32:27 » |
|
I can only crash it on an empty string or nothing between the ( and the ).
So... although there are a few nasty hacks, I can't break it anymore with valid input.
|
|
|
|
CommanderKeith
|
 |
«
Reply #16 - Posted
2009-03-16 01:05:44 » |
|
Wow!!!!!!!! Thanks heaps Woogley! That's amazing, it works perfect!  I can't believe you just went and made it. Your powers need harnessing! I'm using it to make a program that helps uni students learn finance. I'm tutoring 3 classes and I can see that they hate actually doing questions because working them out on a calculator is a pain in the butt, but doing it in an MS-excel style formula bar is easy. They'll have to work out questions like "What's the net present value of paying $100 now and recieving $40 every year forever, starting in 2 years from now? The discount rate is 10%." Then in that funky formula bar you made they can type "-100 + (40/0.1)/(1.1^2)" and read the output then select the right answer from a choice of 4. I'll send you a link to it when I'm finished. Thanks Riven and irreversible_kev for helping.
|
|
|
|
Abuse
|
 |
«
Reply #17 - Posted
2009-03-16 02:17:38 » |
|
"1^--1" gives "empty String" error.
As it appears to have no problem evaluating "--1" on it's own, so perhaps a bug? (though I can see why it might not be considered a bug, as it won't accept "1^+1" either - though it gives a more meaningful error)
|
|
|
|
Abuse
|
 |
«
Reply #18 - Posted
2009-03-16 02:33:43 » |
|
I don't think "*1" should be considered a legal expression. (Neither "/1" or "^1" are valid)
Also, if "--1" is a valid input "+-1" should also be valid, shouldn't it?
|
|
|
|
woogley
|
 |
«
Reply #19 - Posted
2009-03-16 03:27:48 » |
|
@CommanderKeith Awesome, I'm glad it's working for you  I'd be very interested to see how you are using it "1^--1" gives "empty String" error.
As it appears to have no problem evaluating "--1" on it's own, so perhaps a bug? (though I can see why it might not be considered a bug, as it won't accept "1^+1" either - though it gives a more meaningful error)
Yeah, I didn't write the parser thinking anyone would use + as non-op (to denote a number as positive). I've rewritten it now so the expressions you mentioned work now I don't think "*1" should be considered a legal expression. (Neither "/1" or "^1" are valid)
Also, if "--1" is a valid input "+-1" should also be valid, shouldn't it?
Also fixedThanks for pointing this out edit: whoops, not fixed. 0+-1 actually crashes the parser with no error  edit 2: All of the bugs above have been fixed
|
|
|
|
|
woogley
|
 |
«
Reply #20 - Posted
2009-03-16 03:59:13 » |
|
Thanks for pitching in everyone, so far all of the bugs mentioned in this thread have been resolved. Since most of the debugging/testing was done by multiple people, development time for this was just a mere ~4 hours @CommanderKeith .. sorry for stealing your thread  Hopefully the snippet will make up for it Quick recap, since it's several posts up: - The MathEval code can be found here- You can test some expressions with this self-executable JARIf anyone else can break it, please share
|
|
|
|
|
oNyx
|
 |
«
Reply #21 - Posted
2009-03-16 04:22:55 » |
|
The test jar lacks some action listener for that text field. If you add it one can just press return at the end. 
|
|
|
|
CommanderKeith
|
 |
«
Reply #22 - Posted
2009-03-16 06:20:22 » |
|
It's getting better and better. Nice job getting it done in such a short time. You should put it up in shared code when it's finished, I'm sure it would be of use to many people, especially considering JEP costs $550 ( http://www.singularsys.com/order/) and people probably only need it for the kind of calcs that you're one can do.
|
|
|
|
cylab
|
 |
«
Reply #23 - Posted
2009-03-16 12:00:54 » |
|
This is great, thanks for sharing. Variables and constants would be great though 
|
Mathias - I Know What [you] Did Last Summer!
|
|
|
Riven
|
 |
«
Reply #24 - Posted
2009-03-16 13:34:30 » |
|
I might opensource my parser, which also has variables and multiple-argument functions (bound to Java methods). It was my futile attempt at making a fancy scripting language. 
|
|
|
|
pjt33
|
 |
«
Reply #25 - Posted
2009-03-16 13:44:50 » |
|
If anyone prefers a JavaCC grammar, this is largely based on the calculator in the tutorial and handles all the test cases I've seen in this thread. It also supports numbers in the format 2.6E-17. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
| options { STATIC = false ; }
PARSER_BEGIN(Calculator) import java.io.PrintStream ;
class Calculator { public static void main( String[] args ) throws ParseException, TokenMgrError, NumberFormatException { Calculator parser = new Calculator(System.in); parser.Start( System.out ); } } PARSER_END(Calculator)
SKIP : { " " } TOKEN : { < EOL : "\n" | "\r" | "\r\n" > } TOKEN : { < PLUS : "+" > } TOKEN : { < MINUS : "-" > } TOKEN : { < TIMES : "*" > } TOKEN : { < DIVIDE : "/" > } TOKEN : { < POW : "^" > } TOKEN : { < OPEN_PAR : "(" > } TOKEN : { < CLOSE_PAR : ")" > } TOKEN : { < NUMBER : <FP> | <FP> "E" <DIGITS> | <FP> "E-" <DIGITS> > } TOKEN : { < #FP : <DIGITS> | <DIGITS> "." <DIGITS> | <DIGITS> "." | "." <DIGITS> > } TOKEN : { < #DIGITS : (["0"-"9"])+ > }
void Start(PrintStream printStream) throws NumberFormatException : { double value; } { ( value=Expression() <EOL> {printStream.println(value);} | <EOL> {} )* <EOF> }
double Expression() throws NumberFormatException : { double i ; double value ; } { value=Term() ( <PLUS> i=Term() {value += i;} | <MINUS> i=Term() {value -= i;} )* {return value;} }
double Term() throws NumberFormatException : { double i ; double value ; } { value=Exponential() ( <TIMES> i=Exponential() {value *= i;} | <DIVIDE> i=Exponential() {value /= i;} | i=NonUnaryExponential() {value *= i;} )* {return value;} }
double Exponential() throws NumberFormatException: { double i; double value; } { value=Primary() ( <POW> i=Primary() {value = Math.pow(value, i);} )* {return value;} }
double NonUnaryExponential() throws NumberFormatException: { double i; double value; } { value=NonUnaryPrimary() ( <POW> i=Primary() {value = Math.pow(value, i);} )* {return value;} }
double NonUnaryPrimary() throws NumberFormatException : { Token t; double d; } { t=<NUMBER> {return Double.parseDouble(t.image);} | <OPEN_PAR> d=Expression() <CLOSE_PAR> {return d;} }
double Primary() throws NumberFormatException : { double d ; } { d=NonUnaryPrimary() {return d;} | <PLUS> d=Primary() {return d;} | <MINUS> d=Primary() {return -d;} } |
|
|
|
|
|
CommanderKeith
|
 |
«
Reply #26 - Posted
2009-03-17 12:54:00 » |
|
Cool, the more options the better!
Thanks everyone for the help.
|
|
|
|
Markus_Persson
|
 |
«
Reply #27 - Posted
2009-03-17 13:57:23 » |
|
You can try it out here, to see if you can break it, make it calculate the wrong answer, etc:
It claims 2^3^4 is 4096. It should be 2.41785164E24.
|
|
|
|
cylab
|
 |
«
Reply #28 - Posted
2009-03-17 14:44:11 » |
|
It claims 2^3^4 is 4096. It should be 2.41785164E24.
I see your point, but thats debatable. Do math parsers using the ^ notation usually treat it as being written [size=13pt] 2[/size][size=9pt]3[/size] 4or (2 3) 4?
|
Mathias - I Know What [you] Did Last Summer!
|
|
|
pjt33
|
 |
«
Reply #29 - Posted
2009-03-17 15:07:29 » |
|
Wikipedia suggests it should be right-associative, so here's a tweaked version of the JavaCC grammar: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127
| options { STATIC = false ; }
PARSER_BEGIN(Calculator) import java.io.PrintStream ;
class Calculator { public static void main( String[] args ) throws ParseException, TokenMgrError, NumberFormatException { Calculator parser = new Calculator(System.in); parser.Start( System.out ); } } PARSER_END(Calculator)
SKIP : { " " } TOKEN : { < EOL : "\n" | "\r" | "\r\n" > } TOKEN : { < PLUS : "+" > } TOKEN : { < MINUS : "-" > } TOKEN : { < TIMES : "*" > } TOKEN : { < DIVIDE : "/" > } TOKEN : { < POW : "^" > } TOKEN : { < OPEN_PAR : "(" > } TOKEN : { < CLOSE_PAR : ")" > } TOKEN : { < NUMBER : <FP> | <FP> "E" <DIGITS> | <FP> "E-" <DIGITS> > } TOKEN : { < #FP : <DIGITS> | <DIGITS> "." <DIGITS> | <DIGITS> "." | "." <DIGITS> > } TOKEN : { < #DIGITS : (["0"-"9"])+ > }
void Start(PrintStream printStream) throws NumberFormatException : { double value; } { ( value=Expression() <EOL> {printStream.println(value);} | <EOL> {} )* <EOF> }
double Expression() throws NumberFormatException : { double i ; double value ; } { value=Term() ( <PLUS> i=Term() {value += i;} | <MINUS> i=Term() {value -= i;} )* {return value;} }
double Term() throws NumberFormatException : { double i ; double value ; } { value=Exponential() ( <TIMES> i=Exponential() {value *= i;} | <DIVIDE> i=Exponential() {value /= i;} | i=NonUnaryExponential() {value *= i;} )* {return value;} }
double Exponential() throws NumberFormatException: { double i; double value; } { value=Primary() ( <POW> i=Exponential() {return Math.pow(value, i);} | {return value;} ) }
double NonUnaryExponential() throws NumberFormatException: { double i; double value; } { value=NonUnaryPrimary() ( <POW> i=Exponential() {return Math.pow(value, i);} | {return value;} ) }
double NonUnaryPrimary() throws NumberFormatException : { Token t; double d; } { t=<NUMBER> {return Double.parseDouble(t.image);} | <OPEN_PAR> d=Expression() <CLOSE_PAR> {return d;} }
double Primary() throws NumberFormatException : { double d ; } { d=NonUnaryPrimary() {return d;} | <PLUS> d=Primary() {return d;} | <MINUS> d=Primary() {return -d;} } |
|
|
|
|
|
|