Fast low memory consuming mathematical evaluation without endless string parsing! Parses string formula once and uses its object sequence in each evaluation. Moreover provides user defined functions and variables.
Microsoft.Extensions.DependencyInjection
- Features
- Performance
- Quick start
- Extra types
- Variables
- Operators
- Boolean operators
- Functions
- Options
- Documentation
- Thanks for contribution

- TODO
- Fast math evaluation
- Zero-allocation code (object pooling)
- User defined functions
- User defined variables with any chars
- Mixed result type
- Scientific notation
Compared with a good solution mXparser
- In general, x5 faster!
| â„– | Formula |
|---|---|
| Empty | |
| NumberOnly | 3 |
| 1 | 3 * 9 |
| 2 | 3 * 9 / 456 * 32 + 12 / 17 - 3 |
| 3 | 3 * (9 / 456 * (32 + 12)) / 17 - 3 |
| 4 | (2 + 6 - (13 * 24 + 5 / (123 - 364 + 23))) - (2 + 6 - (13 * 24 + 5 / (123 - 364 + 23))) + (2 + 6 - (13 * 24 + 5 / (123 - 364 + 23))) * 345 * ((897 - 323)/ 23) |
| 5 | Arg1 * Arg2 + Arg3 - Arg4 |
| 6 | Arg1 * (Arg2 + Arg3) - Arg4 / (Arg5 - Arg6 + 1) + 45 * Arg7 + ((Arg8 * 56 + (12 + Arg9))) - Arg10 |
| 7 | add(1; 2; 3) |
| 8 | add(add(5; 1) - add(5; 2; 3)) |
| 9 | if(Arg1 > 0; add(56 + 9 / 12 * 123.596; or(78; 9; 5; 2; 4; 5; 8; 7); 45;5); 9) * 24 + 52 -33 |
| 10 | kov(1; 2; 3) - kovt(8; 9) |
As you can see this solution is faster in all cases, furthermore there isn't any garbage collection.
Benchmark code - src/ConsoleApp/Benchmark/BenchNumbers.cs
Benchmark excel - benchResults/Benchmark.xlsx
There are two ways to use evaluator:
- Static initialization
public void SomeMethod()
{
var facade = NoStringEvaluator.CreateFacade();
var evaluator = facade.Evaluator;
}- DI from the package
public void ConfigureServices(IServiceCollection services)
{
// ......
services.AddNoStringEvaluator();
}Add INoStringEvaluator to your controller, service, etc...
And just send string or FormulaNodes to evaluation:
public class MyService
{
private INoStringEvaluator _noStringEvaluator;
public MyService(INoStringEvaluator noStringEvaluator)
{
_noStringEvaluator = noStringEvaluator;
}
public double CalcNumber(string formula)
{
return _noStringEvaluator.CalcNumber(formula);
}
public string CalcWord(string formula)
{
return _noStringEvaluator.CalcWord(formula);
}
public EvaluatorValue Calc(string formula)
{
return _noStringEvaluator.Calc(formula);
}
}If you have variables, you can send IDictionary or your IVariablesContainer implementation:
public class MyService
{
private INoStringEvaluator _noStringEvaluator;
public MyService(INoStringEvaluator noStringEvaluator)
{
_noStringEvaluator = noStringEvaluator;
}
public double Calc(string formula, IDictionary<string, EvaluatorValue> variables)
{
return _noStringEvaluator.CalcNumber(formula, variables);
}
}If you need your function, just implement the interface IFunction If you want to returnt extra type, use factory.
As an argument's separator can be:
- ;
- ,
For instance, usage function "YouAre('Vitaly'; 26)":
public class MyFunction : IFunction
{
public string Name { get; } = "YouAre";
public bool CanHandleNullArguments { get; }
public InternalEvaluatorValue Execute(List<InternalEvaluatorValue> args, ValueFactory factory)
{
var name = args[0].Word;
var age = args[1];
var ageAfterDecade = age + 10;
var result = $"Hello, {name}. After 10 years you will be {ageAfterDecade} y.o.";
return factory.Word.Create(result);
}
}And don't forget to initialize your functions via options or directly in IFunctionReader
public void SomeMethod()
{
// NoStringEvaluator.CreateFacade(opt => opt.WithFunctionsFrom(<type from source assembly>));
// NoStringEvaluator.CreateFacade(opt => opt.WithFunctionsFrom(<source assembly>));
// NoStringEvaluator.CreateFacade(opt => opt.WithFunctions(new MyFunction()));
// same with DI
// services.AddNoStringEvaluator(opt => opt.WithFunctions(new MyFunction()));
}Apart from double calculations you can work with types:
- Boolean
- DateTime
- String
- List of string
- List of double
- Null
- Object
Object is a special type to allow using, for example, services inside function.
public void Should_Evaluate_Service()
{
// arrange
var service = _serviceFactory(null);
var args = new Dictionary<string, EvaluatorValue>
{
["myService"] = new EvaluatorValue(new MyService()),
["myNum"] = 10
};
var expected = 50.5;
// act
var actual = service.CalcNumber("TestService(myService; myNum)", args);
// assert
actual.Should().BeApproximatelyNumber(expected);
}
private class ServiceFunction : IFunction
{
public string Name { get; } = "TestService";
public bool CanHandleNullArguments { get; }
public InternalEvaluatorValue Execute(List<InternalEvaluatorValue> args, ValueFactory factory)
{
return args[0].GetObject<MyService>().GetTemperature() + args[1];
}
}
private class MyService
{
public double GetTemperature()
{
return 40.5;
}
}You can describe a list inside the formula
| Example | Result |
|---|---|
| IsMember({'printer', 'computer', 'monitor'}; 'computer') | 1 |
| Unique({'NEW','OLD','NEW','HEAVEN','OLD'}) | {'NEW','OLD','HEAVEN'} |
| Add({1, 2, 3, 10, 3}) | 19 |
You can use two types of variables:
- Simple variable
- Bordered variable
Simple variable means that it named without unique symbols and starts with a letter. Only one extra symbol is possible, it's "_"
Some examples:
- "25 + myArgument - 1"
- "25 + myArg1 - 2"
- "arg5684argArg_arg"
- "25 + myArgument_newAge - 3"
Bordered variable means that it has a tricky name with any symbols, except for square brackets.
Some examples:
- "25 + [myVariable and some words] - 1"
- "25 + [Provider("my provider").Month(1).Price] - 2"
- "[myVariable ♥]"
- "[simpleVariable]"
Needless to say, you can write simple variable with brackets as well.
There are some known variables, you shouldn't send them to Calc method.
| Key word | Description | Value |
|---|---|---|
| pi | Pi, Archimedes' constant or Ludolph's number | 3.14159265358979323846 |
| tau | A circle constant equal to 2Ď€ | 6.283185307179586476925 |
| e | Napier's constant, or Euler's number, base of Natural logarithm | 2.7182818284590452354 |
| true | Boolean True | True |
| false | Boolean False | False |
| ASC | Boolean True | True |
| DESC | Boolean False | False |
These variables are register independent, you can write Pi, [PI], pI, True, etc...
| Key word | Description | Example |
|---|---|---|
| + | Addition | a + b |
| - | Subtraction | a - b |
| * | Multiplication | a * b |
| / | Division | a / b |
| ^ | Exponentiation | a^b |
| Key word | Description | Example |
|---|---|---|
| < | Lower than | a < b |
| <= | Lower or equal | a <= b |
| > | Greater than | a > b |
| >= | Greater or equal | a >= b |
| == | Equality | a == b |
| = | Equality | a = b |
| != | Inequation | a != b |
| <> | Inequation | a <> b |
| && | Logical conjunction (AND) | a && b |
| || | Logical disjunction (OR) | a || b |
| ! | Negation | !IsNull(a) |
| Key word | Description | Example |
|---|---|---|
| add | Summation operator | add(a1; a2; ...; an) can include List |
| multi | Multiplication | multi(a1; a2; ...; an) can include List |
| mean | Mean / average value | mean(a1; a2; ...; an) can include List |
| min | Minimum function | min(a; b) can include List |
| max | Maximum function | max(a; b) can include List |
| Rpund | Rounds the designated number to the specified decimals | Round(a; decimals) |
| ln | Natural logarithm function (base e) | ln(x) |
| log | Logarithm function (base b) | log(a; b) |
| log2 | Binary logarithm function (base 2) | log2(x) |
| log10 | Common logarithm function (base 10) | log10(x) |
| sqrt | Squre root function | sqrt(x) |
| abs | Absolut value function | abs(x) |
| sgn | Signum function | sgn(x) |
| sign | Signum function | sign(x) |
| floor | Floor function | floor(x) |
| ceil | Ceiling function | ceil(x) |
| mod | Modulo function | mod(a; b) |
| fact | Factorial function | fact(x) |
| fib | Fibonacci number | fib(x) |
| gcd | Greatest common divisor | gcd(a1; a2; ...; an) |
| lcm | Least common multiple | lcm(a1; a2; ...; an) |
| Key word | Description | Example |
|---|---|---|
| sin | Trigonometric sine function | sin(x) |
| cos | Trigonometric cosine function | cos(x) |
| tg | Trigonometric tangent function | tg(x) |
| tan | Trigonometric tangent function | tan(x) |
| ctg | Trigonometric cotangent function | ctg(x) |
| cot | Trigonometric cotangent function | cot(x) |
| ctan | Trigonometric cotangent function | ctan(x) |
| sec | Trigonometric secant function | sec(x) |
| csc | Trigonometric cosecant function | csc(x) |
| cosec | Trigonometric cosecant function | cosec(x) |
| asin | Inverse trigonometric sine function | asin(x) |
| arsin | Inverse trigonometric sine function | arsin(x) |
| arcsin | Inverse trigonometric sine function | arcsin(x) |
| acos | Inverse trigonometric cosine function | acos(x) |
| arcos | Inverse trigonometric cosine function | arcos(x) |
| arccos | Inverse trigonometric cosine function | arccos(x) |
| atg | Inverse trigonometric tangent function | atg(x) |
| atan | Inverse trigonometric tangent function | atan(x) |
| arctg | Inverse trigonometric tangent function | arctg(x) |
| arctan | Inverse trigonometric tangent function | arctan(x) |
| actg | Inverse trigonometric cotangent function | actg(x) |
| acot | Inverse trigonometric cotangent function | acot(x) |
| actan | Inverse trigonometric cotangent function | actan(x) |
| arcctg | Inverse trigonometric cotangent function | arcctg(x) |
| arccot | Inverse trigonometric cotangent function | arccot(x) |
| arcctan | Inverse trigonometric cotangent function | arcctan(x) |
| sinh | Hyperbolic sine function | sinh(x) |
| cosh | Hyperbolic cosine function | cosh(x) |
| tgh | Hyperbolic tangent function | tgh(x) |
| tanh | Hyperbolic tangent function | tanh(x) |
| coth | Hyperbolic cotangent function | coth(x) |
| ctgh | Hyperbolic cotangent function | ctgh(x) |
| ctanh | Hyperbolic cotangent function | ctanh(x) |
| sech | Hyperbolic secant function | sech(x) |
| csch | Hyperbolic cosecant function | csch(x) |
| cosech | Hyperbolic cosecant function | cosech(x) |
| arcsec | Inverse trigonometric secant | arcsec(x) |
| asinh | Inverse hyperbolic sine function | asinh(x) |
| arsinh | Inverse hyperbolic sine function | arsinh(x) |
| arcsinh | Inverse hyperbolic sine function | arcsinh(x) |
| acosh | Inverse hyperbolic cosine function | acosh(x) |
| arcosh | Inverse hyperbolic cosine function | arcosh(x) |
| arccosh | Inverse hyperbolic cosine function | arccosh(x) |
| atgh | Inverse hyperbolic tangent function | atgh(x) |
| atanh | Inverse hyperbolic tangent function | atanh(x) |
| arctgh | Inverse hyperbolic tangent function | arctgh(x) |
| arctanh | Inverse hyperbolic tangent function | arctanh(x) |
| acoth | Inverse hyperbolic cotangent function | acoth(x) |
| actgh | Inverse hyperbolic cotangent function | actgh(x) |
| actanh | Inverse hyperbolic cotangent function | actanh(x) |
| arccoth | Inverse hyperbolic cotangent function | arccoth(x) |
| arcctgh | Inverse hyperbolic cotangent function | arcctgh(x) |
| arcctanh | Inverse hyperbolic cotangent function | arcctanh(x) |
| asech | Inverse hyperbolic secant function | asech(x) |
| arsech | Inverse hyperbolic secant function | arsech(x) |
| arcsech | Inverse hyperbolic secant function | arcsech(x) |
| acsch | Inverse hyperbolic cosecant function | acsch(x) |
| arcsch | Inverse hyperbolic cosecant function | arcsch(x) |
| arccsch | Inverse hyperbolic cosecant function | arccsch(x) |
| acosech | Inverse hyperbolic cosecant function | acosech(x) |
| arcosech | Inverse hyperbolic cosecant function | arcosech(x) |
| rad | Degrees to radians function | rad(x) |
| deg | Radians to degrees function | deg(x) |
| exp | Exponential function | exp(x) |
| Key word | Description | Example |
|---|---|---|
| if | If function | if(cond; expr-if-true; expr-if-false) |
| iff | If function | iff( cond-1; expr-1; ... ; cond-n; expr-n ) |
| and | Logical conjunction (AND) | and(a1; a2; ...; an) |
| or | Logical disjunction (OR) | or(a1; a2; ...; an) |
| not | Negation function | not(x) |
| IsNaN | Returns true if value is a Not-a-Number (NaN) | isNaN(x) |
| IsError | Returns true if this is a double.NaN | IsError(ToNumber('Text')) |
| IsMember | Checks if second argument is a member of list from first | IsMember({'printer', 'computer', 'monitor'}; 'computer') |
| IsNumber | Returns true if this is a number | IsNumber(256) |
I've implemented some of excel functions. If you wanna see more, just send me a message.
| Key word | Description | Example |
|---|---|---|
| Count | Returns a number of elements | Count(a; b; ...) can include List |
| Len | Returns the number of characters in a text string | Len("my word") |
| Sort | Sorts a List. sortType: true - asc, false - desc | Sort(myList; sortType) |
| ToNumber | Returns number from word | ToNumber('03') |
| Key word | Description | Example |
|---|---|---|
| DateDif | Calculates the number of days, months, or years between two dates. Can be: Y, M, D | DateDif(date1; date2; 'Y') |
| TimeDif | Calculates the number of hours, minutes, or seconds between two dates. Can be: H, M, S | DateDif(time1; time2; 'H') |
| Now | Returns Datetime.Now | Now() |
| Today | Returns the current date | Today() |
| Day | Returns a day from dateTime | Day(Now()) Day(Now(); 'DD') |
| Month | Returns a month from dateTime | Month(Now()) Month(Now(); 'MM') |
| Year | Returns a year from dateTime | Year(Now()) Year(Now(); 'YY') |
| ToDateTime | Returns datetime value from string | ToDateTime('8/15/2002') |
| WeekDay | Takes a date and returns a number between 1-7 representing the day of week | WeekDay(Today()) |
| DateFormat | Format dateTime to string | DateFormat(Now(); 'HH:mm:ss') |
| AddHours | Adds a number of hours to a datetime | AddHours(date; 17) |
| AddMinutes | Adds a number of minutes to a datetime | AddMinutes(date; 17) |
| AddSeconds | Adds a number of seconds to a datetime | AddSeconds(date; 17) |
| Key word | Description | Example |
|---|---|---|
| Concat | Concates values | Concat(56; ' myWord') Concat(myList; myArg; 45; myList2) |
| Explode | Returns a text list composed of the elements of a text string. Separator by default is white space " " | Explode(myWord) Explode(myWord; separator) |
| Implode | Concatenates all members of a text list and returns a text string. separator by default is empty "" | Implode(myList) Implode(myList; separator) Implode(myList; 5; 'my wordd'; separator) last value is separator |
| Left | Searches a string from left to right and returns the leftmost characters of the string | Left(myWord) Left(myWord; numberOfChars) Left(myWord; wordNeededChars) |
| Middle | Returns any substring from the middle of a string | Middle(myWord; indexStart; numberChars) Middle(myWord; indexStart; wordEnd) Middle(myWord; wordStart; numberChars) Middle(myWord; wordStart; wordEnd) |
| Right | Searches a string from right to left and returns the rightmost characters of the string | Right(myWord) Right(myWord; numberOfChars) Right(myWord; wordNeededChars) |
| Lower | Converts text to lowercase | Lower(myWord) Lower(myWordList) |
| Upper | Converts text to uppercase | Upper(myWord) Upper(myWordList) |
| Proper | Capitalizes the first letter in each word of a text | Proper(myWord) |
| Replace | Replaces characters within text | Replace(myWord; oldPart; newPart) Replace(myList; oldPart; newPart) |
| Text | Returns text from first argument | Text(26) |
| Unique | If second parameter is true then returns only unique If second parameter is false then returns list without doubles | Unique(myList) Unique(myList; true) |
| IsText | Returns true if this is a word | IsText('my word') |
| Key word | Description | Example |
|---|---|---|
| IfNull | Returns second argument if the first is null | IfNull(x,3) |
| NullIf | Returns null if the first argument is equal to the second | NullIf(x,3) |
| IsNull | Returns null if the first argument null | IsNull(x) |
When you use AddNoStringEvaluator or CreateFacade you can configure evaluator.
To illustrate, I change floating point from default dot to comma:
public void ConfigureServices(IServiceCollection services)
{
// ......
services.AddNoStringEvaluator(opt => opt.SetFloatingPointSymbol(FloatingPointSymbol.Comma));
}| Method | Description |
|---|---|
| FormulaNodes Parse(string formula) | Return parsed formula nodes |
| FormulaNodes Parse(ReadOnlySpan formula) | Return parsed formula nodes |
| List ParseWithoutRpn(ReadOnlySpan formula) | Return parsed formula nodes without RPN |
| Method | Description |
|---|---|
| void AddFunction(IFunction func, bool replace = false) | Add function |
| void RemoveFunction(string functionName) | Remove function |
| Method | Description |
|---|---|
| FormulaNodes GetFormulaNodes(string formula) | Return cached formula nodes |
| Method | Description |
|---|---|
| CheckFormulaResult CheckSyntax(string formula) | Check syntax |
| CheckFormulaResult CheckSyntax(ReadOnlySpan formula) | Check syntax |
| Method | Description |
|---|---|
| IVariable AddOrUpdate(string name, double value) | Add or update variable |
| EvaluatorValue GetValue(string name) | Return variable's value |
| bool TryGetValue(string name, out EvaluatorValue value) | Return variable's value if possible |
| Method | Description |
|---|---|
| string Name { get; } | Name of function |
| bool CanHandleNullArguments { get; } | If false and any argument is null - function wont be executed and null will be returned |
| InternalEvaluatorValue Execute(List args, ValueFactory factory) | Evaluate value |
| Method | Description |
|---|---|
| SetWordQuotationSingleQuote() | Set word quotation mark - ' |
| SetWordQuotationDoubleQuote() | Set word quotation mark - " |
| SetWordQuotationMark(string mark) | Set word quotation mark |
| SetFloatingTolerance(double floatingTolerance) | Set floating tolerance |
| SetFloatingPointSymbol(FloatingPointSymbol floatingPointSymbol) | Set floating point symbol |
| SetThrowIfVariableNotFound(bool isThrow) | Set throw if variable not found |
| WithFunctionsFrom(Type typeFromSourceAssembly) | Add assembly to register functions |
| WithFunctionsFrom(Assembly sourceAssembly) | Add assembly to register functions |
| WithoutDefaultFunctions(bool withoutDefaultFunctions = true) | Remove root assembly from functions registration |
| WithFunctions(params IFunction[] functions) | Add functions |
| Method |
|---|
| double CalcNumber(string formula, IVariablesContainer variables) |
| double CalcNumber(FormulaNodes formulaNodes, IVariablesContainer variables) |
| double CalcNumber(string formula, IDictionary<string, EvaluatorValue> variables) |
| double CalcNumber(FormulaNodes formulaNodes, IDictionary<string, EvaluatorValue> variables) |
| double CalcNumber(string formula) |
| double CalcNumber(FormulaNodes formulaNodes) |
| string CalcWord(... |
| DateTime CalcDateTime(... |
| List CalcWordList(... |
| List CalcNumberList(... |
| bool CalcBoolean(... |
| EvaluatorValue Calc(... |
Referenced methods are same as in INoStringEvaluator
| Method |
|---|
| double? CalcNumber(string formula, IVariablesContainer variables) |
| DateTime? CalcDateTime(string formula, IVariablesContainer variables) |
| bool? CalcBoolean(string formula, IVariablesContainer variables) |
Static initialization if you don't have DI
| Method | Description |
|---|---|
| static Facade CreateFacade(Action<NoStringEvaluatorOptions> options = null) | Create evaluator facade |
Static initialization if you don't have DI
| Method | Description |
|---|---|
| static Facade CreateFacade(Action<NoStringEvaluatorOptions> options = null) | Create evaluator facade |
- Add more functions
- Any idea?


