pieterderycke / jace Goto Github PK
View Code? Open in Web Editor NEWJace.NET is a calculation engine for the .NET platform.
License: MIT License
Jace.NET is a calculation engine for the .NET platform.
License: MIT License
Hello,
(New here on Github. )
I was looking for a calculation engine for my project and yours is very good. However, when trying out the compile-time constant example (image attached) I noticed that the documentation for the same does not match the Master branch released version.
Seems like that feature is still in the dev branch.
Thought to bring it to your notice.
Also, I saw that you are incorporating .Net Core as well and planning a release for 1.0 version. Is there an estimate on when the version 1.0 would be released?
Thank you for everything.
Regards,
The library has floor, ceiling, and truncate, but not round. Can we get round added?
Not Getting Result when giving variable name use with underscore like => "NT_CUM_NT"
Hi,
I'm trying to parse the following expression a + (d <= d1 ? b * -0.01 : (d <= d2 ? b * 0.02 + c * 0.03 : 0))
However it fails saying it doesn't expect the multiplication
Jace.ParseException
Unexpected floating point constant "-0.01" found.
at Jace.AstBuilder.VerifyResultStack()
at Jace.AstBuilder.Build(IList`1 tokens)
at Jace.CalculationEngine.BuildAbstractSyntaxTree(String formulaText)
at Jace.CalculationEngine.Build(String formulaText)
Any idea on how I could achieve this ?
I provided expression $1+$2+$3
and variables list as follows
$1=0
$2=0
$3=0
it should return 0, but it returned 6.
With a freshly downloaded and cleanly built Jace.NET solution (2c0033a) and using Mono 3.2.8 on Lubuntu 14.04, running the Jace.Benchmark project causes a crash of some sort during the last "Compiled Mode" section just after the threads are started. Reliably reproducible for me.
Here is a transcript of the application output, including assertion failure message and stacktrace:
Loaded assembly: /home/bb/Jace-master/Jace.Benchmark/bin/Debug/Jace.Benchmark.exe
Loaded assembly: /home/bb/Jace-master/Jace.Benchmark/bin/Debug/Jace.dll
Jace.NET Benchmark Application
--------------------
Function: 2+3*7/23
Number Of Tests: 1,000,000
Interpreted Mode:
Loaded assembly: /usr/lib/mono/gac/System.Core/4.0.0.0__b77a5c561934e089/System.Core.dll [External]
Loaded assembly: /usr/lib/mono/gac/System/4.0.0.0__b77a5c561934e089/System.dll [External]
Total duration: 00:00:05.3861940
Compiled Mode:
Loaded assembly: Anonymously Hosted DynamicMethods Assembly [External]
Total duration: 00:00:05.3827540
--------------------
Function: (var1 + var2 * 3)/(2+3) - something
Number Of Tests: 1,000,000
Interpreted Mode:
Total duration: 00:00:04.4561450
Compiled Mode:
Total duration: 00:00:03.5664070
--------------------
Random Generated Functions: 1,000
Number Of Variables Of Each Function: 3
Number Of Executions For Each Function: 10,000
Total Number Of Executions: 10,000,000
Parallel: True
Interpreted Mode:
Thread started: #2
Thread started: #3
Thread finished: #2
Thread finished: #3
Total duration: 00:00:28.6792790
Compiled Mode:
Thread started: #4
Thread started: #5
* Assertion at mini-codegen.c:2307, condition `sp < 8' not met
Stacktrace:
at <unknown> <0xffffffff>
at (wrapper managed-to-native) System.Delegate.CreateDelegate_internal (System.Type,object,System.Reflection.MethodInfo,bool) <IL 0x00024, 0xffffffff>
at System.Delegate.CreateDelegate (System.Type,object,System.Reflection.MethodInfo,bool,bool) <IL 0x00344, 0x00caf>
at System.Delegate.CreateDelegate (System.Type,System.Reflection.MethodInfo,bool) <IL 0x00005, 0x00043>
at System.Delegate.CreateDelegate (System.Type,System.Reflection.MethodInfo) <IL 0x00003, 0x0002f>
at System.Reflection.Emit.DynamicMethod.CreateDelegate (System.Type) <IL 0x00032, 0x000d3>
at Jace.Execution.DynamicCompiler.BuildFormulaInternal (Jace.Operations.Operation,Jace.Execution.IFunctionRegistry) [0x0003d] in /home/bb/Jace-master/Jace/Execution/DynamicCompiler.cs:47
at Jace.Execution.DynamicCompiler.BuildFormula (Jace.Operations.Operation,Jace.Execution.IFunctionRegistry) [0x00017] in /home/bb/Jace-master/Jace/Execution/DynamicCompiler.cs:30
at Jace.CalculationEngine/<BuildFormula>c__AnonStorey0.<>m__0 (string) [0x0001c] in /home/bb/Jace-master/Jace/CalculationEngine.cs:303
at Jace.Util.MemoryCache`2/<GetOrAdd>c__AnonStorey0.<>m__0 (TKey) [0x00013] in /home/bb/Jace-master/Jace/Util/MemoryCache.cs:137
at System.Collections.Concurrent.ConcurrentDictionary`2/<GetOrAdd>c__AnonStorey3.<>m__0 () <IL 0x00012, 0x00039>
at (wrapper delegate-invoke) System.Func`1<System.Collections.Generic.KeyValuePair`2<string, Jace.Util.MemoryCache`2/CacheItem<string, System.Func`2<System.Collections.Generic.Dictionary`2<string, double>, double>>>>.invoke_TResult__this__ () <IL 0x00054, 0xffffffff>
at System.Collections.Concurrent.SplitOrderedList`2<string, System.Collections.Generic.KeyValuePair`2<string, Jace.Util.MemoryCache`2/CacheItem<string, System.Func`2<System.Collections.Generic.Dictionary`2<string, double>, double>>>>.ListInsert (System.Collections.Concurrent.SplitOrderedList`2/Node<string, System.Collections.Generic.KeyValuePair`2<string, Jace.Util.MemoryCache`2/CacheItem<string, System.Func`2<System.Collections.Generic.Dictionary`2<string, double>, double>>>>,System.Collections.Concurrent.SplitOrderedList`2/Node<string, System.Collections.Generic.KeyValuePair`2<string, Jace.Util.MemoryCache`2/CacheItem<string, System.Func`2<System.Collections.Generic.Dictionary`2<string, double>, double>>>>,System.Collections.Concurrent.SplitOrderedList`2/Node<string, System.Collections.Generic.KeyValuePair`2<string, Jace.Util.MemoryCache`2/CacheItem<string, System.Func`2<System.Collections.Generic.Dictionary`2<string, double>, double>>>>&,System.Func`1<System.Collections.Generic.KeyValuePair`2<string, Jace.Util.MemoryCache`2/CacheItem<string, System.Func`2<System.Collections.Generic.Dictionary`2<string, double>, double>>>>) <IL 0x00068, 0x001cf>
at System.Collections.Concurrent.SplitOrderedList`2<string, System.Collections.Generic.KeyValuePair`2<string, Jace.Util.MemoryCache`2/CacheItem<string, System.Func`2<System.Collections.Generic.Dictionary`2<string, double>, double>>>>.InsertInternal (uint,string,System.Collections.Generic.KeyValuePair`2<string, Jace.Util.MemoryCache`2/CacheItem<string, System.Func`2<System.Collections.Generic.Dictionary`2<string, double>, double>>>,System.Func`1<System.Collections.Generic.KeyValuePair`2<string, Jace.Util.MemoryCache`2/CacheItem<string, System.Func`2<System.Collections.Generic.Dictionary`2<string, double>, double>>>>,System.Collections.Concurrent.SplitOrderedList`2/Node<string, System.Collections.Generic.KeyValuePair`2<string, Jace.Util.MemoryCache`2/CacheItem<string, System.Func`2<System.Collections.Generic.Dictionary`2<string, double>, double>>>>&) <IL 0x00039, 0x0014f>
at System.Collections.Concurrent.SplitOrderedList`2<string, System.Collections.Generic.KeyValuePair`2<string, Jace.Util.MemoryCache`2/CacheItem<string, System.Func`2<System.Collections.Generic.Dictionary`2<string, double>, double>>>>.InsertOrGet (uint,string,System.Collections.Generic.KeyValuePair`2<string, Jace.Util.MemoryCache`2/CacheItem<string, System.Func`2<System.Collections.Generic.Dictionary`2<string, double>, double>>>,System.Func`1<System.Collections.Generic.KeyValuePair`2<string, Jace.Util.MemoryCache`2/CacheItem<string, System.Func`2<System.Collections.Generic.Dictionary`2<string, double>, double>>>>) <IL 0x00008, 0x0006f>
at System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd (TKey,System.Func`2<TKey, TValue>) <IL 0x00058, 0x0022e>
at Jace.Util.MemoryCache`2.GetOrAdd (TKey,System.Func`2<TKey, TValue>) [0x0003e] in /home/bb/Jace-master/Jace/Util/MemoryCache.cs:133
at Jace.CalculationEngine.BuildFormula (string,Jace.Operations.Operation) [0x00028] in /home/bb/Jace-master/Jace/CalculationEngine.cs:303
at Jace.CalculationEngine.Build (string) [0x00042] in /home/bb/Jace-master/Jace/CalculationEngine.cs:159
at Jace.Execution.FormulaBuilder.Build () [0x0002f] in /home/bb/Jace-master/Jace/Execution/FormulaBuilder.cs:80
at Jace.Benchmark.Program/<BenchMarkCalculationEngineRandomFunctionBuild>c__AnonStorey0.<>m__0 (string) [0x00034] in /home/bb/Jace-master/Jace.Benchmark/Program.cs:128
at System.Threading.Tasks.Parallel/<ForEach>c__AnonStorey6`1.<>m__0 (TSource,System.Threading.Tasks.ParallelLoopState,object) <IL 0x00007, 0x0002d>
at System.Threading.Tasks.Parallel/<ForEach>c__AnonStorey5`2.<>m__0 () <IL 0x000e5, 0x00394>
at System.Threading.Tasks.TaskActionInvoker/ActionInvoke.Invoke (System.Threading.Tasks.Task,object,System.Threading.Tasks.Task) <IL 0x00006, 0x00029>
at System.Threading.Tasks.Task.InnerInvoke () <IL 0x0003f, 0x000ad>
at System.Threading.Tasks.Task.ThreadStart () <IL 0x00098, 0x002eb>
at System.Threading.Tasks.Task.Execute () <IL 0x00001, 0x00027>
at System.Threading.Tasks.TpScheduler.<QueueTask>m__0 (object) <IL 0x00006, 0x00043>
at System.Threading.Thread.StartInternal () <IL 0x0003c, 0x000bf>
at (wrapper runtime-invoke) object.runtime_invoke_void__this__ (object,intptr,intptr,intptr) <IL 0x0004e, 0xffffffff>
Native stacktrace:
/usr/bin/mono() [0x8105b4a]
[0xb771740c]
[0xb7717424]
/lib/i386-linux-gnu/libc.so.6(gsignal+0x47) [0xb750c827]
/lib/i386-linux-gnu/libc.so.6(abort+0x143) [0xb750fc53]
/usr/bin/mono() [0x8288b23]
/usr/bin/mono() [0x8288bb3]
/usr/bin/mono() [0x8100a10]
/usr/bin/mono() [0x80659b9]
/usr/bin/mono() [0x8066dd1]
/usr/bin/mono() [0x8068de4]
/usr/bin/mono() [0x8069aee]
/usr/bin/mono() [0x8186398]
[0xb5bf62e4]
[0xb5bf4b30]
[0xb5bf3e54]
[0xb5bf3de0]
[0xb5bf3584]
[0xb5846180]
[0xb5845f44]
[0xb584388a]
[0xb58431d9]
[0xb5842eea]
[0xb5843078]
[0xb5bc2bb0]
[0xb5842c68]
[0xb5842a80]
[0xb584282f]
[0xb58423b8]
[0xb5842174]
[0xb5bf858c]
[0xb5bf8260]
[0xb561d20c]
[0xb561ce7e]
[0xb561af2d]
[0xb561ab82]
[0xb561ab36]
[0xb561a584]
[0xb561a278]
[0xb561a21c]
[0xb561a1a0]
[0xb5e1b18d]
/usr/bin/mono() [0x8069bf0]
Debug info from gdb:
Could not attach to process. If your uid matches the uid of the target
process, check the setting of /proc/sys/kernel/yama/ptrace_scope, or try
again as the root user. For more details, see /etc/sysctl.d/10-ptrace.conf
ptrace: Operation not permitted.
No threads.
=================================================================
Got a SIGABRT while executing native code. This usually indicates
a fatal error in the mono runtime or one of the native libraries
used by your application.
=================================================================
Unary minus has higher precedence than power operator:
var result = engine.Calculate("-1^2");
The result must become -1 but becomes +1 instead.
It would be helpfully to have an boolean option, where you can switch trigonomic functions (sin, cos, tan, ...) to calculate with degrees and not with radians.
I think this would be easy to implement...
I get "Invalid token" error when I use Jace in specific CultureInfo programs.
For example, I get this error on the character "," in a in if clause when I use the Italian Culture.
I saw from your blog (Pieter) that you plan to rev to 1.0 when you add a few more features. Does it really need to wait for those?
Most people are nervous about using something that has a version number less than 1 (ok.. they're typically nervous for 1 as well, but less so).
This looks like a quality library. I like the test coverage. But I noticed it doesn't have as many downloads as I would expect.
Do you think the number of downloads would increase if it had a version that signals, "This is released"?
Looking for an alternative to NCALC and come across to this package, but wondered if tis still maintained or updated?
Does this project works with .NET5 and the upcoming .NET6? If it doesnt, can it be updated? How soon?
Hi there, found this project by looking for alternative to NCalc. Seems to work for me, but i am finding a very high memory issue with it and i see that performance on this project seems very good according to the readme and wiki pages.
One thing i need to know if that is it possible to override the operators? I ask this because if i have + for example, i want to check first if one of the values is NULL, and if so return NULL.
Great library, but I'm missing to calculate with percent %. An easy example, 100+20% don't give a result.
Hello :) ,
I got two questions.
How do i get the actual parsed function? Not the result.
Do you have a example for working with matrix?
Is it possible to have support for scientific notation?
I'd like to be able to write something like
engine.Build("var1+2.11E-3");
At the moment the error message I get is "The syntax of the provided formula is not valid."
Similarly
engine.Build("2.11E-3");
gives "Unexpected floating point constant "2.11" found."
Thanks
The Euler number e is also used for scientific notation. This leads to some problems. E.g.
"-e"
fails with a IndexOutOfBounds exception in TokenReader.Read(string formula).
Do you have any plan to add support variable as complex number, i? Since .NET 4.0 or .NET 4.5, Microsoft start introduce System.Numerics.Complex, and Imaginary number.
This is very useful especially in composing math expression in electronic engineering computing. Or you can give me some tips maybe I can try to fork your repo and extend myself. Or should I go for NCalc for more easier implementation? (As I know, NCalc also not support yet).
I have formulas like this:
$Load1/(4*$g)
in which "$Load1" and "$g" are variables.
I get "Invalid token "$" detected at position 0" when I try to run Jace with that.
Why "$" is not accepted as part of the variable name?
thanks,
Giovanni
I think the title makes it pretty clear.
I'd like to define variables with special characters.
Now I'm using the _ (underscore) character to differentiate two type of variables. But I need to differentiate a third type of variable and I'd like to use characters like | or # or : or ? etc.
Can you add the support of one of this characters for the name of a variable?
Thank you
Running the following throws and exception, that the variable "pi" was not found:
CalculationEngine engine = new CalculationEngine(CultureInfo.InvariantCulture, ExecutionMode.Interpreted);
Func<Dictionary<string, double>, double> formula = engine.Build("pi");
Dictionary<string, double> variables = new Dictionary<string, double>();
double result = formula(variables);
However, when running this with ExecutionMode.Compiled
it works as expected.
The difference is, that Interpreter
that is used in ExecutionMode.Interpreted
does not check for constants as DynamicCompiler
does that is used in ExecutionMode.Compiled
:
private static class PrecompiledMethods
{
public static double GetVariableValueOrThrow(string variableName, FormulaContext context)
{
if (context.Variables.TryGetValue(variableName, out double result))
return result;
else if (context.ConstantRegistry.IsConstantName(variableName))
return context.ConstantRegistry.GetConstantInfo(variableName).Value;
else
throw new VariableNotDefinedException($"The variable \"{variableName}\" used is not defined.");
}
}
I will create a pull request where this check of the ConstantRegistry is added to the Interpreter
.
When testing out your example:
CalculationEngine engine = new CalculationEngine();
Func<Dictionary<string, double>, double> formula = engine.Build("var1+2/(3*otherVariable)");
Dictionary<string, double> variables = new Dictionary<string, double>();
variables.Add("var1", 2);
variables.Add("otherVariable", 4.2);
double result = formula(variables);
I get an exception that says "othervariable is not defined." If I change 'variables.Add("otherVariable",4.2");' to 'variables.Add("othervariable",4.2");' then it works fine. The variables in the formula seem to be getting converted to lower case. Can you confirm this bug? I am using version 0.8.3.
If this could be upgraded to NetStandard 2.0 at some point, it would be greatly appreciated. Thx!
First of all: Thanks for the very good and performant mathematical formula parser.
When only entering "." as a formula or any other invalid floating point number like "..", "..1", "0..1" the TokenReader will skip this, causing the AstBuilder to throw an InvalidOperationException because it expects the token list not to be empty. The token reader should throw an ParseException in this case.
It seems like square brackets are ignored during tokenization, so perhaps they could be used to allow a user to enter a variable name that is any string they'd like, e.g. with spaces, numbers at the beginning, etc.
Not sure how I'd make this change myself, but I'm looking into it when I have a chance.
Is this supported?
I have a need to run Jace in a .NET Core app.
I could not use the Nuget version as it targets the .NET Framework.
So I checked out the Portable library. I ran Visual Studio Portability plugin tool over this project at it gave a 100% pass for .NET Standard.
Whilst I can use the portable lib in my .NET Core library now, I'd love to be able to link it to a NuGet package so I can receive any future updates.
Is this a feasible thing to do ? Or are there some things about the Portable project I'm not aware of ?
For the project I'm working on, a client needs to be able to enter variable names with underscores in them, like "var_1" instead of "var1". They could come at any part of the name.
I made a stab at adjusting TokenReader.cs to allow this. On line 161, I added || character == '_'
to the or-chain. It seems to work well, but I'm not yet familiar with the entirety of the tokenization mechanism to be absolutely certain there aren't other things that would need changing.
Currently at the line mentioned below it can be seen that the DynamicCompiler
returns a Func
that makes a call to EngineUtil.ConvertVariableNamesToLowerCase
. When calling Calculate
on the CalculationEngine
it has already converted all variable names to lowercase. Ideally, this would be a feature one could opt-out of altogether since users interested in maximizing performance might be enforcing lowercase variable names already.
Jace/Jace/Execution/DynamicCompiler.cs
Line 42 in f97eac1
If my performance metrics from a project using your nuget package are correct, ~33% of the time executing a calculation (through the CalculationEngine.Build
method, so we are already avoiding the repeated call to EngineUtil.ConvertVariableNamesToLowerCase
in the CalculationEngine.Calculate
method) for us is taken up by ensuring that the variables are all lowercased.
Hi,
Is it possible to add constant vars when build a function? something like that:
engine.build("a+b+c").Parameter("a",1").Parameter("b",2").Build();
Or I need to replace constant variable by myself before create a function?
engine.build("1+2+c").Build();
tks
I tried to use this project in Unity
but I found out that Jace uses System.Reflection.Emit
this is not allowed in most of the platforms as specified by Unity
https://docs.unity3d.com/Manual/ScriptingRestrictions.html
Is there any workaround to this?
Hello!
Great library, however i am missing native bitwise operations support.
For example bitwise shifting (>> and <<) or bitwise inversion (~)
They can be substituted with functions, but having them as supported operations would be nice
We built against 0.9.3 and added our own implementation of "random" that is different. We'd like to keep it. If it were overridable that would be ideal.
When entering an invalid scientific notation number e.g. 3e
causes an IndexOutOfBoundsException instead of a ParseException.
Dear Pieter,
Thanks for sharing this great library!
I hope you can help me with a small issue I'm facing: I've noticed that the math formula is sensitive to the UI culture, i.e. in my culture (Dutch) I would like to parse 0.5, but this generates a parse error, so instead I have to use 0,5.
How should I proceed when I would always like to use the former version? For example, can I somewhere specify CurrentCulture.InvariantCulture when parsing formulas?
Thanks, Erik
I stumbled across this problem when entering the normal distribution. I could narrow the problem down to the following issue:
-(1)^2 is interpreted as (-1)^2 giving 1 as a result although it should be -1.
The same happens when parsing -1^2 which is equal to -(1)^2.
This is because the precedence of the operations is wrong. I will provide a PR.
support for string variables, comparison and evaluation. Example:
if(var_1=='A', 'Match', 'No Match')
Hi,
I cannot understand ho to use the IF statement:
1 - like this: DT=if(Se==8,D/te,10000000)
2 - or like this: if(Se==8,DT=D/te,DT=10000000)
both of them do not work. Is it a bug or I did something wrong?
Is the ThreadEngine meant to be thread safe so I can use it as a singleton accross threads?
Hello again
Another feature that would be nice is support for non base 10 integers
For example 0x for hex and 0b for binary
There is an error on the documentation related to the standard function IF.
The description of the function and the example provided uses the comma (,) as operator separator. It seems that the valid separator is ;
Therefore the correct information for the IF function must be as follow:
The example provided must be changed to:
engine.Calculate("if(var1 < var2; 23; 8)", variables);
I'm using commit 2c0033a.
Probably less than 10% of the time that I run Jace.Benchmarks (in a Debug configuration), in the parallel part during either the interpreted or compiled subpart, there is an unhandled NullReferenceException
from the orderby
lambda within the keysToDelete
query composition in the Jace.Util.MemoryCache<TKey, TValue>.EnsureCacheStorageAvailable
method.
When this happens, the current p
(a KeyValuePair<TKey, CacheItem>
structure) has both Key
and Value
equal to null
, like a default zeroed structure had been retrieved rather than any of the previously added KeyValuePair
structures (which seem to always be added to the ConcurrentDictionary
with non-null members). The call to LastAccessed
seems to be the thrower.
To try to reliably see this error, you can make the following source modification: In Jace.Benchmarks.Program, starting around line 50, adjust the number of random generated functions to 10,000 and the number of executions per function to 1,000. After I do this, I can reliably reproduce the above-mentioned error in every run I launch.
On line 157 of MemoryCache.cs, replacing the query source dictionary
with dictionary.ToArray()
(which uses ConcurrentDictionary.ToArray
, not Enumerable.ToArray
) seems to totally resolve the problem by thread-safely taking a snapshot of dictionary
before the query is composed and executed.
Based on the relatively little I've reviewed at the .NET 4.5.1 reference source for Enumerable's query operators, the race condition seems like it might be due to (a) the query's source enumerable being dictionary
itself rather than a snapshot and (b) one thread's dictionary.TryRemove
(MemoryCache.cs:167) occurring between some enumerator object's MoveNext
and Current
calls on another thread (the enumerator being some one of the several that exist in the operator call-chain upon query execution) when executing ToList
on the composed query (MemoryCache.cs:159).
I'm not (or not yet) super-familiar with LINQ or CLR threading, though, so this explanation might be wrong or at least incomplete.
Wouldn't it be more efficient to register the function like this:
FunctionRegistry.RegisterFunction("sin", (Func<double, double>)Math.Sin, false);`
Instead of
FunctionRegistry.RegisterFunction("sin", (Func<double, double>)((a) => Math.Sin(a)), false);
Thinking of that it would be one less function call.
But I may be mistaken.
According to Wikipedia:
In mathematics, a square root of a number a is a number y such that
y^2 = a
; in other words, a numbery
whose square (the result of multiplying the number by itself, or y ⋅ y) is a. For example, 4 and −4 are square roots of 16 because 42 = (−4)2 = 16.
To facilitate this, an Operation
would be need to handle processing and returning two double
s. Each operation would have to decide if it will operate on the double and possibly filter out double.NaN
.
I see this as a major overhaul and a major breaking change, and would require a bump in the major version if following semver.
Once that overhaul is complete, it would be trivial to return new Tuple<double, double>(Math.Sqrt(x), -Math.Sqrt(x))
for the Sqrt function operation. Alternatively, a new internal model could be passed around that includes the Edit: This would not work as the next operation may alter the negation result differently and that information would need to be communicated to further operations and eventually the caller.double
result as well as a flag that indicates the next operation should be performed on both the result and the negation of the result.
Thoughts?
A change was recently made to add support for scientific notation. I believe in doing so we lost the ability to use the built-in variable e
.
Attempts to use any formula using the variable e
as previously done now fail. e.g.
var engine = new CalculationEngine(CultureInfo.InvariantCulture, ExecutionMode.Compiled);
var result = engine.Calculate("e");
When using more complex functions (1 * e
) you receive a rather unclear error message:
There is a syntax issue for the operation \"*\" at position 2. The number of arguments does not match with what is expected.
Reverting the following line change seems to avoid the issue, but I am sure will also prevent the scientific notation support from functioning properly:
Jace/Jace/Tokenizer/TokenReader.cs
Line 224 in 25b8d70
I don't know if Jace is designed to be culture-dependent, but I think that the syntax found in this Wiki must be equal for all users, indipendently from the OS culture.
I forced this in TokenReader.cs by using:
//this.decimalSeparator = cultureInfo.NumberFormat.NumberDecimalSeparator[0]; this.decimalSeparator = '.'; //this.argumentSeparator = cultureInfo.TextInfo.ListSeparator[0]; this.argumentSeparator = ',';
Just to share with everyone.
The calculus (570.8957 + 114.1791) - (543.75 + 108.75) returns 32.574800000000096 on Jace however it should return 32.5748.
Thanks!
What effort, would you guess, is required to get Jace to support decimal (not just double)?
Evan
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.