How to parse & process mathematical expressions in JavaScript Calculator? - javascript

Whats the best approach with javascript to evaluate an expression like 3+1*sin(20)*e^3?
First of all, I do not want to use built-in math-functions. In fact, I have already defined my own functions for power, square root, sin, cos, exponential, etc.
What I'm looking for, is a good way to parse an expression so I can pass them to these functions I created. I know there is an eval() function available in JavaScript for evaluating input expressions, but I'm not sure if that's suitable for this context.
Should I use eval()? If yes, how do I use it in this context? If no, what's a more suitable way to parse mathematical expressions?!
It's not an school assignment, its just a doubt.

Using a third party library
In a real life situation, you'd want to use a library like math.js to do the calculations for you.
To use this library, first include it in your HTML :
<script src='path/to/math.js'></script>
Then, you can just use the math.eval method to evaluate your expression :
math.eval('3+1*sin(20)*e^3');
That is, if 20 is a value expressed in radians. As it's probably a value expressed in degrees, you'd need to make this small adjustment :
math.eval('3+1*sin(20 deg)*e^3');
Because you explicitly said that you do not want to use the built-in math-functions, I suspect you're working on a school assignment. Note that submitting school assignments on StackOverflow is frowned upon, as it is considered a form of cheating.
But let's just ignore this and move on, shall we?!
Doing it on your own
Step 1 : Calculation with built-in math functions
In real life projects you should ALWAYS use built-in math functions or third party libraries (like math.js) that use built-in math functions under the hood. That's because built-in math functions are both 100% reliable and performance optimized.
It's impossible to create your own equivalent of built-in math functions in a way that is both as reliable and as performance optimized as the built-in functions, so there is NEVER a good reason NOT to use the built-in functions. But since you explicitly asked for it, let's ignore that and look at how to do things WITHOUT the built-in math functions. I assume this also implied you do not want to use math.js or any other library.
Using JavaScript's built-in functions, you'd calculate your expression like this :
3+1*Math.sin(20)*Math.pow(Math.E,3)
Built-in functions used :
Math.sin(x) can be used to calculate the sin of x
Math.pow(x, n) can be used to calculate xⁿ
Math.E() is a constant that represents e
Here, again, it's important to state this is true only if 20 is a value in radians. As 20 is probably a value in degrees, you probably want to define your own degree based sin function like this :
var dsin = function() {
var piRatio = Math.PI / 180;
return function dsin(degrees) {
return Math.sin(degrees * piRatio);
};
}();
Now, replace Math.sin with dsin
3+1*dsin(20)*Math.pow(Math.E,3)
In any real world application, this would be the best was to calculate 3+1*sin(20)*e^3!
Step 2 : Replacting built-in math functions with your own
Now, as you REALLY, REALLY, REALLY seem to want to go without built-in functions, just the next step is to replace every built-in function with a self-written equivalent. Often, there are multiple mathematical paths towards the same goal.
For example, you could write your pow function recursively like this :
var pow = function(base, exp) {
if (exp == 0)
return 1;
else
return base * power(base, exp - 1);
}
An iterative implementation :
var pow = function(base, exp) {
var result = 1;
while(exp--) {
result *= base;
}
return result;
}
So now what, you ask?! How can you implement dsin without relying on Math.sin, you ask?!
Well... A little bit of research tells you that you can calculare sin, using the formula sin(x) = x - x^3 / 3! + x^5 / 5! - x^7 / 7! + x^9 / 9! - ...
In JavaScript, that would be something like this :
var sin = function(x) {
var t1 = -1,t2 = 3,v1 = x,v2 = v1 + 1,it = 0;
while (it < 10) {
v2 = v1 + (t1 * pow(x, t2) / function(n){
j = 1;
for(i=1;i<=n;i++)
j = j*i;
return j;
}(t2));
t1 = -1 * t1;
t2 += 2;
v1 = v2;
it++;
}
return v2;
}
You might notice that I put my factorial calculation in a lambda function. A more elegant solution would be to put your lambda function in a separate function, as you're likely to need it elsewhere.
Now, you just need to puzzle all the pieces together and wrap them in a nice object :
var Calc = (function(){
return {
E : 2.718281828459045,
pow : function (base, exp) {
var result = 1;
while (exp--) {
result *= base;
}
return result;
},
fact : function(n){
j = 1;
for(i=1;i<=n;i++)
j = j*i;
return j;
},
sin : function(x) {
var t1 = -1,t2 = 3,v1 = x,v2 = v1 + 1,it = 0;
while (it < 10) {
v2 = v1 + (t1 * this.pow(x, t2) / this.fact(t2));
t1 = -1 * t1;
t2 += 2;
v1 = v2;
it++;
}
return v2;
},
dsin : function() {
var piRatio = Math.PI / 180;
return function dsin(degrees) {
return Math.sin(degrees * piRatio);
};
}()
};
})();
You can use the methods of these objects like this :
3+1*Calc.dsin(20)*Calc.pow(Calc.E,3)
Step 3 : Parsing
If you actually want your application to read an expression like 3+1*sin(20)*e^3 directly, you should NOT use eval(). Instead, you should write your own parser.
The proper way to do this, is to first write a tokenizer that converts your expression to a syntax tree. Then, you pass your syntax tree to an interpreter that processes your syntax tree by running the calculation functions you've created in step 1.
For an in depth look into how to build a syntax tree and interpreter, you could take a look at this tutorial.

I wrote a parser that does what you need. It's called SwanJS and it doesn't use eval or new Function.
Installation:
npm install #onlabsorg/swan-js
Usage:
const swan = require('#onlabsorg/swan-js'); // It works also in the browser
const evaluate = swan.parse("3 + 1 * sin(20) * e**3");
const context = swan.createContext({
sin: Math.sin, // or your own sin function
e: Math.E
});
const value = await evaluate(context);
If you don't like ** as power operator, you may change it, but that's not well documented. Look at /lib/interpreter.js to see how to define a new grammar or ask me if you prefer.

If you want more mathematical functionalities in addition to the standard JavaScript math library, then you may want to use a third party library like math.js.
To use this library, first include it in your HTML :
<script src='path/to/math.js'></script>
Then, you can just use the math.eval method to evaluate your expression :
math.eval('3+1*sin(20)*e^3');

Related

Can I use variables in JS `e` numbers?

Per 'e' in javascript numbers I would like to use 'e' notation:
Right now I'm doing:
const USD_DECIMALS = 2
const USD = 1e2
As you can see I'm duplicating the number 2. When I'd like to do is something like:
const USD_DECIMALS = 2
const USD = 1e[USD_DECIMALS]
Is this possible? Maybe using the Number() constructor or similar?
You could make a string out of "1e2" and then run it through Number.
const USD_DECIMALS = 2
const USDString = `1e${USD_DECIMALS}`;
const USD = Number(USDString);
console.log(USD);
Not directly (without building a dynamic string, but then that's not direct either), but since 1e2 means 1 times 10², you can use either 1 * Math.pow(10, USD_DECIMALS) OR 1 * 10**USD_DECIMALS to do the same thing dynamically:
const USD_DECIMALS = 2;
const USD = 1 * 10**USD_DECIMALS;
// Or: const USD = 1 * Math.pow(10, USD_DECIMALS);
Live Example:
console.log("Various ways to get 10²:");
console.log(1e2);
const USD_DECIMALS = 2;
console.log(1 * Math.pow(10, USD_DECIMALS));
console.log(1 * 10**USD_DECIMALS);
console.log("Just for completeness, 10³:");
console.log(1e3);
const USD_DECIMALS3 = 3;
console.log(1 * Math.pow(10, USD_DECIMALS3));
console.log(1 * 10**USD_DECIMALS3);
.as-console-wrapper {
max-height: 100% !important;
}
(Side note: you sometimes need () around ** expressions where you may not expect to, such as in -1 ** 10, but not in the above.)
Just being a bit silly: you could add an e method to numbers. It's never a good idea to augment native prototypes in library code, but it can be okay in app-specific or page-specific code that doesn't have to integrate with unknown other code like libraries do, provided you do it correctly (make the extension non-enumerable). So FWIW:
Object.defineProperty(Number.prototype, "e", {
value(exp) {
return this * 10**exp;
},
// The usual set of flags for methods on built-in prototypes
// (`enumerable: false` is the default, but including it here
// just for emphasis)
writable: true,
configurable: true,
enumerable: false,
});
const USD_DECIMALS = 2;
console.log(1..e(USD_DECIMALS));
console.log((1).e(USD_DECIMALS));
let value = 1; // or whatever
console.log(value.e(USD_DECIMALS));
I probably wouldn't do it for this. Partially because I lean toward leaving native prototypes alone even in app-/page-specific code, and also because of the awkwardness of calling methods on numeric literals. Notice how I had to use 1..e(USD_DECIMALS) or (1).e(USD_DECIMALS) instead of just 1.e(USD_DECIMALS). That's because the . after a series of digits is part of the numeric literal, so it doesn't start a property/method access. The extra . solves that, or wrapping the literal in (). There's no problem when using a variable for the base value, since that's not a numeric literal, so value.e(USD_DECIMALS) works. So on the whole, for me, not worth it, but a bit of fun to look at.
The NeM is for the order of magnitude (N*10^M). What you can do is just
n=n*(10**m)
If you actually want to control the number of decimals you have to round.

Evaluate an Equation in Javascript, without eval() [duplicate]

This question already has answers here:
Evaluating a string as a mathematical expression in JavaScript
(26 answers)
Calculate string value in javascript, not using eval
(12 answers)
Safe evaluation of arithmetic expressions in Javascript
(5 answers)
How to code a calculator in javascript without eval
(1 answer)
Eval alternative
(4 answers)
Closed 5 days ago.
I have a bunch of fields in a web page (150+) that need to have equations run on them to produce a result.
I currently store the equation like this:
<input name="F7" type="text" class="numeric" data-formula="([C7]-[D7])/[E7]" readonly />
When an input is blurred, I use a jQuery selector to iterate over all inputs with a data-formula attribute, take the formula, and use regex to replace the pointers (the [C7] in the equation) with their appropriate values.
After that, I eval() the equation to get a result, and put it in the correct input. This works great, but is very slow and results in the web page hanging for a few seconds, which is bad if it happens every time an input is blurred.
Is there a way to evaluate an equation, such as "(1-2)/4", without using eval()? These equations also may have functions, such as square root (which makes eval() nice, since I can just put Math.sqrt() in the formula), and the numbers may be decimals.
Note: This application must run on IE7 and 8, so I don't believe I can use Webworkers or anything like that. I have also considered only running this code after a "Save" button is hit, but I would prefer the UI to update live if possible
I only really know two alternatives, one is to use a script element that is dynamically written to the page, e.g.:
function evaluate(formula)
{
var script = document.createElement("script");
script.type = "text/javascript";
script.text = "window.__lr = " + formula + ";";
document.body.appendChild(script);
document.body.removeChild(script);
var r = window.__lr;
return r;
}
The other would be to use new Function(...):
function evaluate3(formula)
{
var func = new Function("return " + formula);
return func();
}
But I don't think you'll find something that yields similar performance to eval: http://jsperf.com/alternative-evaluation
The performance of eval varies across browsers and platforms, have you got a specific browser/platform combination in mind? The newer javascript engines in improved browsers will offer optimised eval:
This is only a limited set of tests on a few UAs, but it should give you an idea of how it performs in different environments.
Is there a way to evaluate an equation, such as "(1-2)/4", without using eval()?
Well, you can tokenize the expression and write your own evaluator that mimics what eval does. But while that might be useful in terms of limiting the side-effects (since eval is a very big hammer), it's extremely unlikely to perform better than eval does.
What you can do, though, is cache the result of evaluating all the other inputs so that you only evaluate the input the actually blurred. That should be quite efficient indeed.
For example, suppose you had this global object:
var values = {
A7: /* initial value for A7 */,
B7: /* initial value for B7 */,
C7: /* initial value for C7 */,
D7: /* initial value for D7 */,
E7: /* initial value for E7 */,
F7: /* initial value for F7 */,
/* etc */
};
...and then attached this blur handler to all inputs:
$("input").blur(function() {
values[this.id] = this.value; // Or parseInt(this.value, 10), or parseFloat(this.value), etc.
doTheEvaluation();
});
...where doTheEvaluation used the values from values rather than recalculating all of them every time.
If this.value might refer to other fields, you could do a recursive evaluation of it — but without evaluating all of your inputs.
I do realize this answer is 8 years too late, but I thought I would add my own contribution since this issue came up in a project I was working on. In my case, I am using Nodejs, but this solution should work for a browser as well.
let parens = /\(([0-9+\-*/\^ .]+)\)/ // Regex for identifying parenthetical expressions
let exp = /(\d+(?:\.\d+)?) ?\^ ?(\d+(?:\.\d+)?)/ // Regex for identifying exponentials (x ^ y)
let mul = /(\d+(?:\.\d+)?) ?\* ?(\d+(?:\.\d+)?)/ // Regex for identifying multiplication (x * y)
let div = /(\d+(?:\.\d+)?) ?\/ ?(\d+(?:\.\d+)?)/ // Regex for identifying division (x / y)
let add = /(\d+(?:\.\d+)?) ?\+ ?(\d+(?:\.\d+)?)/ // Regex for identifying addition (x + y)
let sub = /(\d+(?:\.\d+)?) ?- ?(\d+(?:\.\d+)?)/ // Regex for identifying subtraction (x - y)
/**
* Evaluates a numerical expression as a string and returns a Number
* Follows standard PEMDAS operation ordering
* #param {String} expr Numerical expression input
* #returns {Number} Result of expression
*/
function evaluate(expr)
{
if(isNaN(Number(expr)))
{
if(parens.test(expr))
{
let newExpr = expr.replace(parens, function(match, subExpr) {
return evaluate(subExpr);
});
return evaluate(newExpr);
}
else if(exp.test(expr))
{
let newExpr = expr.replace(exp, function(match, base, pow) {
return Math.pow(Number(base), Number(pow));
});
return evaluate(newExpr);
}
else if(mul.test(expr))
{
let newExpr = expr.replace(mul, function(match, a, b) {
return Number(a) * Number(b);
});
return evaluate(newExpr);
}
else if(div.test(expr))
{
let newExpr = expr.replace(div, function(match, a, b) {
if(b != 0)
return Number(a) / Number(b);
else
throw new Error('Division by zero');
});
return evaluate(newExpr);
}
else if(add.test(expr))
{
let newExpr = expr.replace(add, function(match, a, b) {
return Number(a) + Number(b);
});
return evaluate(newExpr);
}
else if(sub.test(expr))
{
let newExpr = expr.replace(sub, function(match, a, b) {
return Number(a) - Number(b);
});
return evaluate(newExpr);
}
else
{
return expr;
}
}
return Number(expr);
}
// Example usage
//console.log(evaluate("2 + 4*(30/5) - 34 + 45/2"));
In the original post, variables may be substituted using String.replace() to provide a string similar to the example usage seen in the snippet.
I would modify your code to perform only one eval.
var expressions = []
// for each field
// expressions.push("id:" + parsedExpression);
var members = expressions.join(",");
var resultObj = eval("({" + members + "})");
// for each field
document.getElementById(id).value = resultObj[id];
Validation: I'd write a powerful Regular expression to validate the input, then use eval to evaluate it if it's safe.
Evaluation: Regarding the speed of eval: If it's a big problem, you could queue up all equations (store it in an array), and evaluate them all at once:
var equations = ['1+1', '2+2', '...']; //<-- Input from your fields
var toBeEvald = '[' + equations.join(',') + '];';
var results = eval(toBeEvald);
// result[0] = 2
// result[1] = 4, etc
If you had a reliable internet connection, you could connect to google and use their services to evaluate an expression. Google has a pretty powerful server, and all you would have to do is send a request with the queue being the equation and retrieve it. Of course, this could be slower or faster depending on internet speed/browser speed.
Or, you can write your own equation evaluator. This is pretty difficult, and probably won't be any more efficient than eval. You'd also have to go through the immense trouble of the PEMDAS order.
I suggest you could merge the equations together into one string, and eval that all at once, and retrieve the results all at once.
You can use new Function to evaluate your expressions

Golomb sequence

I'm trying to resolve a small programming challenge, which is calculating the nth number of the Golomb's sequence (see this for more help). I've written a simple solution, but it may have any problem, because the number at 2500000 position is 10813 but my program gives me 10814.
var golomb = (function(){
var cache = [null, 1];
const o = 0.5 * (1 + Math.sqrt(5)); // Golden ratio
return function(n){
return cache[n] || (function(){
return Math.round(Math.pow(o, 2-o) * Math.pow(n, o-1));
})();
};
})();
var num = golomb(process.argv[2]);
console.log(num);
Maybe, the golden ratio needs more lenght than JavaScript gives. Someone can help? Thanks.
For what it's worth, here is a function based on the recurrence relation, with a cache, that gives the correct answer pretty quickly
var golomb = (function() {
var cache = [null, 1];
return function(n) {
var i;
for (i=cache.length;i<n;i++) cache[i]=golomb(i);
return cache[n] || (cache[n]=1+golomb(n-golomb(golomb(n-1))));
}
})();
check it up on jsFiddle
Sgmonda, the formula you got from Wolfram Alpha isn't an exact solution. I've actually complained to them, since I like the Golomb sequence. The recurrence relation is exact but slow, even if you cache it. Heh, that's why the programming challenge is a challenge.
From the wikipedia article:
Colin Mallows has given an explicit recurrence relation:
a(1) = 1;
a(n + 1) = 1 + a(n + 1 − a(a(n)))
You need to implement your solution in this iterative method that uses integers.
A quick attempt at trying to implement that gives:
function golomb(n) {
if(n == 1) return 1;
else return 1 + golomb(n − golomb(golomb(n-1)));
}

Javascript Convert numbers to different formats or string alternative

UPDATED:
Using javascript or jQuery, how can I convert a number into it's different variations:
eg:
1000000
to...
1,000,000 or 1000K
OR
1000
to...
1,000 or 1K
OR
1934 and 1234
to...
1,934 or -2K (under 2000 but over 1500)
or
1,234 or 1k+ (over 1000 but under 1500)
Can this is done in a function?
Hope this make sense.
C
You can add methods to Number.prototype, so for example:
Number.prototype.addCommas = function () {
var intPart = Math.round(this).toString();
var decimalPart = (this - Math.round(this)).toString();
// Remove the "0." if it exists
if (decimalPart.length > 2) {
decimalPart = decimalPart.substring(2);
} else {
// Otherwise remove it altogether
decimalPart = '';
}
// Work through the digits three at a time
var i = intPart.length - 3;
while (i > 0) {
intPart = intPart.substring(0, i) + ',' + intPart.substring(i);
i = i - 3;
}
return intPart + decimalPart;
};
Now you can call this as var num = 1000; num.addCommas() and it will return "1,000". That's just an example, but you'll find that all the functions create will involve converting the numbers to strings early in the process then processing and returning the strings. (The separating integer and decimal part will probably be particularly useful so you might want to refactor that out into its own method.) Hopefully this is enough to get you started.
Edit: Here's how to do the K thing... this one's a bit simpler:
Number.prototype.k = function () {
// We don't want any thousands processing if the number is less than 1000.
if (this < 1000) {
// edit 2 May 2013: make sure it's a string for consistency
return this.toString();
}
// Round to 100s first so that we can get the decimal point in there
// then divide by 10 for thousands
var thousands = Math.round(this / 100) / 10;
// Now convert it to a string and add the k
return thousands.toString() + 'K';
};
Call this in the same way: var num = 2000; num.k()
Theoretically, yes.
As TimWolla points out, it requires a lot of logic.
Ruby on Rails have a helper for presenting time with words. Have a look at the documentation. The implementation for that code is found on GitHub, and could give you some hint as how to go about implementing this.
I agree with the comment to reduce the complexity by choosing one format.
Hope you find some help in my answer.

Math.random() returns value greater than one?

While playing around with random numbers in JavaScript I discovered a surprising bug, presumably in the V8 JavaScript engine in Google Chrome. Consider:
// Generate a random number [1,5].
var rand5 = function() {
return parseInt(Math.random() * 5) + 1;
};
// Return a sample distribution over MAX times.
var testRand5 = function(dist, max) {
if (!dist) { dist = {}; }
if (!max) { max = 5000000; }
for (var i=0; i<max; i++) {
var r = rand5();
dist[r] = (dist[r] || 0) + 1;
}
return dist;
};
Now when I run testRand5() I get the following results (of course, differing slightly with each run, you might need to set "max" to a higher value to reveal the bug):
var d = testRand5();
d = {
1: 1002797,
2: 998803,
3: 999541,
4: 1000851,
5: 998007,
10: 1 // XXX: Math.random() returned 4.5?!
}
Interestingly, I see comparable results in node.js, leading me to believe it's not specific to Chrome. Sometimes there are different or multiple mystery values (7, 9, etc).
Can anyone explain why I might be getting the results I see? I'm guessing it has something to do with using parseInt (instead of Math.floor()) but I'm still not sure why it could happen.
The edge case occurs when you happen to generate a very small number, expressed with an exponent, like this for example 9.546056389808655e-8.
Combined with parseInt, which interprets the argument as a string, hell breaks loose. And as suggested before me, it can be solved using Math.floor.
Try it yourself with this piece of code:
var test = 9.546056389808655e-8;
console.log(test); // prints 9.546056389808655e-8
console.log(parseInt(test)); // prints 9 - oh noes!
console.log(Math.floor(test)) // prints 0 - this is better
Of course, it's a parseInt() gotcha. It converts its argument to a string first, and that can force scientific notation which will cause parseInt to do something like this:
var x = 0.000000004;
(x).toString(); // => "4e-9"
parseInt(x); // => 4
Silly me...
I would suggest changing your random number function to this:
var rand5 = function() {
return(Math.floor(Math.random() * 5) + 1);
};
This will reliably generate an integer value between 1 and 5 inclusive.
You can see your test function in action here: http://jsfiddle.net/jfriend00/FCzjF/.
In this case, parseInt isn't the best choice because it's going to convert your float to a string which can be a number of different formats (including scientific notation) and then try to parse an integer out of it. Much better to just operate on the float directly with Math.floor().

Categories