exp4j 能够评估实域中的表达式和函数。这是一个小型 (40KB) 库,没有任何外部依赖项,实现了Dijkstra 的 Shutting Yard Algorithm。exp4j 带有一组标准的内置函数和运算符。此外,用户可以创建自定义运算符和函数。
exp4j 能够支持excel中的十几种数据函数和最基础的运算符号,并且用户可以自定义函数和运算符,扩展也比较方便容易,只不过都是使用的double最为的基础数据类型,如果精度特别高的,可以自己可以把源码下载下来,然后对项目进行修改。
官网地址:https://www.objecthunter.net/exp4j/
github: https://github.com/fasseg/exp4j
<dependency>
<groupId>net.objecthuntergroupId>
<artifactId>exp4jartifactId>
<version>0.4.8version>
dependency>
大家在依赖坐标的时候可能会下载不下来,推荐配置上阿里的所有镜像仓库,可以再阿里仓库官网查看
https://developer.aliyun.com/mvn/guide
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 http://maven.apache.org/xsd/settings-1.0.0.xsd">
<localRepository>D:/Maven/repositorylocalRepository>
<mirrors>
<mirror>
<id>aliyun-centralid>
<mirrorOf>centralmirrorOf>
<name>aliyun centralname>
<url>https://maven.aliyun.com/repository/centralurl>
mirror>
<mirror>
<id>aliyun-publicid>
<mirrorOf>publicmirrorOf>
<name>aliyun publicname>
<url>https://maven.aliyun.com/repository/publicurl>
mirror>
<mirror>
<id>aliyun-publicid>
<mirrorOf>jcentermirrorOf>
<name>aliyun jcentername>
<url>https://maven.aliyun.com/repository/publicurl>
mirror>
<mirror>
<id>aliyun-springid>
<mirrorOf>aliyun-springmirrorOf>
<name>aliyun springname>
<url>https://maven.aliyun.com/repository/springurl>
mirror>
<mirror>
<id>aliyun-spring-pluginid>
<mirrorOf>aliyun-spring-pluginmirrorOf>
<name>aliyun spring-pluginname>
<url>https://maven.aliyun.com/repository/spring-pluginurl>
mirror>
<mirror>
<id>aliyun-apache-snapshotsid>
<mirrorOf>aliyun-apache-snapshotsmirrorOf>
<name>aliyun apache-snapshotsname>
<url>https://maven.aliyun.com/repository/apache-snapshotsurl>
mirror>
<mirror>
<id>aliyun-googleid>
<mirrorOf>aliyun-googlemirrorOf>
<name>aliyun googlename>
<url>https://maven.aliyun.com/repository/googleurl>
mirror>
<mirror>
<id>aliyun-gradle-pluginid>
<mirrorOf>aliyun-gradle-plugimirrorOf>
<name>aliyun gradle-pluginname>
<url>https://maven.aliyun.com/repository/gradle-pluginurl>
mirror>
<mirror>
<id>aliyun-jcenterid>
<mirrorOf>aliyun-jcentermirrorOf>
<name>aliyun jcentername>
<url>https://maven.aliyun.com/repository/jcenterurl>
mirror>
<mirror>
<id>aliyun-releasesid>
<mirrorOf>aliyun-releasesmirrorOf>
<name>aliyun releasesname>
<url>https://maven.aliyun.com/repository/releasesurl>
mirror>
<mirror>
<id>aliyun-snapshotsid>
<mirrorOf>aliyun-snapshotsmirrorOf>
<name>aliyun snapshotsname>
<url>https://maven.aliyun.com/repository/snapshotsurl>
mirror>
<mirror>
<id>aliyun-grails-coreid>
<mirrorOf>aliyun-grails-coremirrorOf>
<name>aliyun grails-corename>
<url>https://maven.aliyun.com/repository/grails-coreurl>
mirror>
<mirror>
<id>aliyun-mapr-publicid>
<mirrorOf>aliyun-mapr-publicmirrorOf>
<name>aliyun mapr-publicname>
<url>https://maven.aliyun.com/repository/mapr-publicurl>
mirror>
mirrors>
<profiles>
profiles>
settings>
为了评估一个表达式。ExpressionBuilder类可用于创建能够评估的Expression对象。可以通过调用ExpressionBuilder.function()和ExpressionBuilder.operator()来设置自定义函数和自定义运算符。任何给定的变量都可以通过调用Expression.variable()在 ExpressionBuilder.build( ``)返回的``Expression对象上设置
/**
* SIN函数 一秒计算正弦值
*/
@Test
public void test3() {
Expression e = new ExpressionBuilder("3 * sin(y) - 2 / (x - 2)")
.variables("x", "y")
.build()
.setVariable("x", 2.3)
.setVariable("y", 3.14);
double result = e.evaluate();
System.out.println(result);//-6.66188870791721
}
/**
* 异步计算表达式,巧用LOG函数 按指定底数返回对数
LOG(number,base)
Number 为用于计算对数的正实数。
base 为对数的底数。如果省略底数,则假定其值为10。
*/
@Test
public void test4() throws ExecutionException, InterruptedException {
ExecutorService exec = Executors.newFixedThreadPool(1);
Expression e = new ExpressionBuilder("3log(y)/(x+1)")
.variables("x", "y")
.build().setVariable("x", 2.3).setVariable("y", 3.14);
Future<Double> future = e.evaluateAsync(exec);
double result = future.get();
System.out.println(result);//1.0402025453819654
}
变量名必须以字母或下划线_开头,并且只能包含字母、数字或下划线。
以下是有效的变量名:
varX
_x1
_var_X_1
而1_var_x不是因为它不以字母或下划线开头。
因此,像2cos(yx)这样的表达式将被解释为2max(yx)
/**
*,像2cos(yx)这样的表达式将被解释为2*cos(y*x)
*/
@Test
public void test5() throws ExecutionException, InterruptedException {
double result = new ExpressionBuilder("2cos(xy)")
.variables("x","y") .build
()
.setVariable("x", 0.5d)
.setVariable("y", 0.25d)
.evaluate ();
System.out.println(result);//1.984395334458658
}
自 0.4.6 版起,以下常用常数已添加到 exp4j 并自动绑定:pi, π Math.PI 中定义的 π 值,e欧拉数 e 的值,φ黄金比例 (1.61803398874) 的值)
String expr = "pi+π+e+φ";
double expected = 2*Math.PI + Math.E + 1.61803398874d;
Expression e = new ExpressionBuilder(expr).build();
assertEquals(expected, e.evaluate(),0d);
从 0.3.5 版开始,可以使用科学记数法来表示数字,请参阅 wikipedia。该数字分为有效数/尾数y和``yEx形式的指数x ,计算为y * 10^x。请注意,‘e/E’ 不是运算符,而是数字的一部分,因此无法评估像1.1e-(x*2)这样的表达式。使用精细结构常数α=7.2973525698 * 10^−3的示例:
//科学计数法
@Test
public void test7() {
String expr = "7.2973525698e-3";
double expected = Double.parseDouble(expr);
Expression e = new ExpressionBuilder(expr).build();
assertEquals(expected, e.evaluate(),0d);
System.out.println(expected);//0.0072973525698
}
您可以扩展抽象类 Function 以便在表达式中使用自定义函数。你只需要实现apply(double... args)方法。在以下示例中,创建了一个以 2 为底的对数函数,并在以下表达式中使用。
使用恒等式计算任意底的对数的自定义函数log(value, base) = ln(value)/ln(base)
//自定义函数 logb函数 按指定底数返回对数
@Test
public void test8() {
Function logb = new Function("logb", 2) {
@Override
public double apply(double... args) {
return Math.log(args[0]) / Math.log(args[1]);
}
};
double result = new ExpressionBuilder("logb(8, 2)")
.function(logb)
.build()
.evaluate();
double expected = 3;
assertEquals(expected, result, 0d);
System.out.println(expected);//3.0
}
//自定义4个平均函数 avg函数
@Test
public void test9() {
Function avg = new Function("avg", 4) {
@Override
public double apply(double... args) {
double sum = 0;
for(double arg : args) {
sum += arg;
}
return sum / args.length;
}
};
double result = new ExpressionBuilder("avg(1,2,3,4)").function(avg).build().evaluate();
System.out.println(result);//2.5
double expected = 2.5d;
assertEquals(expected, result, 0d);
}
您可以扩展抽象类Operator以声明用于表达式的自定义运算符,符号是由!,#,§,$,&,;,:,~,<,>,|,= 组成的字符串。. 请注意,添加带有已使用符号的运算符会覆盖任何现有运算符,包括内置运算符。因此可以覆盖例如+运算符。Operator 的构造函数最多接受 4 个参数:
用于此操作的符号(由!,#,§,$,&,;,:,~,<,>,|,=组成的字符串)
如果操作是左关联的
操作的优先级
运算符的操作数(1 或 2)
//自定义运算符,创建用于计算阶乘的自定义运算符
@Test
public void test10() {
Operator factorial = new Operator("!", 1, true, Operator.PRECEDENCE_POWER + 1) {
@Override
public double apply(double... args) {
final int arg = (int) args[0];
if ((double) arg != args[0]) {
throw new IllegalArgumentException("Operand for factorial has to be an integer");
}
if (arg < 0) {
throw new IllegalArgumentException("The operand of the factorial can not be less than zero");
}
double result = 1;
for (int i = 1; i <= arg; i++) {
result *= i;
}
return result;
}
};
double result = new ExpressionBuilder("3!")
.operator(factorial)
.build()
.evaluate();
double expected = 6d;
assertEquals(expected, result, 0d);
}
创建一个自定义运算符逻辑运算符,如果第一个参数大于或等于第二个参数,则返回 1,否则返回 0。
/**
* 创建一个自定义运算符逻辑运算符,如果第一个参数大于或等于第二个参数,则返回 1,否则返回 0。
* @throws Exception
*/
@Test
public void testOperators3() throws Exception {
Operator gteq = new Operator(">=", 2, true, Operator.PRECEDENCE_ADDITION - 1) {
@Override
public double apply(double[] values) {
if (values[0] >= values[1]) {
return 1d;
} else {
return 0d;
}
}
};
Expression e = new ExpressionBuilder("1>=2").operator(gteq)
.build();
double evaluate = e.evaluate();
System.out.println(evaluate);
assertTrue(0d == e.evaluate());
e = new ExpressionBuilder("2>=1").operator(gteq)
.build();
assertTrue(1d == e.evaluate());
}
2 + 22 - 22 * 22 / 22 ^ 2+2 - (-2)2% 2一元减号运算符的优先级低于幂运算符的优先级。这意味着像-1^2这样的表达式被评估为-(1^2)而不是(-1)^2。
exp4j 在尝试除以零时抛出 ArithmeticException。在实现涉及除法的 Operator 或 Function 时,实现者必须确保抛出相应的 RuntimeException。
//Division by zero 除零的异常
@Test
public void test11() {
Operator reciprocal = new Operator("$", 1, true, Operator.PRECEDENCE_DIVISION) {
@Override
public double apply(final double... args) {
if (args[0] == 0d) {
throw new ArithmeticException("Division by zero!");
}
return 1d / args[0];
}
};
Expression e = new ExpressionBuilder("0$").operator(reciprocal).build();
double evaluate = e.evaluate();// <- this call will throw an ArithmeticException
System.out.println(evaluate);//2.5
}
好多函数比较陌生,想了解的自己百度一下吧
abs: absolute value 绝对值acos: arc cosine 反余弦asin: arc sine 反正弦atan: arc tangent 反正切cbrt: cubic root 立方根ceil: nearest upper integer 最接近的上整数cos: cosine 余弦cosh: hyperbolic cosine 双曲余弦exp: euler’s number raised to the power (e^x) 欧拉数的幂 (e^x)floor: nearest lower integer 最接近的下整数log: logarithmus naturalis (base e) 自然对数(以 e 为底)log10: logarithm (base 10) 对数(以 10 为底)log2: logarithm (base 2) 对数(以 2 为底)sin: sinesinh: hyperbolic sinesqrt: square root 双曲正弦tan: tangent 切线tanh: hyperbolic tangent 双曲正切signum: signum function 符号函数从 0.4.0 版本开始,exp4j 增加了一个验证表达式的功能。用户可以调用Expression.validate()来执行相对快速的验证。validate 方法还接受一个布尔参数,指示是否应该检查空变量。
@Test
public void test12() {
Expression e = new ExpressionBuilder("x")
.variable("x")
.build();
ValidationResult res = e.validate();
assertFalse(res.isValid());
assertEquals(1, res.getErrors().size());
e.setVariable("x",1d);
res = e.validate();
assertTrue(res.isValid());
}