说明
其实这个应用场景并不多见,但碍于方法以及代码优秀,网络上也没有出现比较完美的解决方案,于是乎再一次从json-script-rule中扒下来一段代码,分享给大家,有需要的尽管拿去用吧!
提示1:老规矩,先认真读注释说明,看使用的例子,该方法不会造成精度问题,且性能测试良好,采用双链表结构加递归处理巧妙解决问题
提示2:java8以上的版本存在一些新的语法如getFirst(),如果你的版本是8那么可以大胆的替换成get(0)
提示3:calculate有多个重载方法,注意方法中调用的是递归calculate还是下面抽离出去的calculate
/**
* <p>计算字符串中的公式,公式必须是合理的公式,不能有除运算符外的其它字符包括空格
* <p>例子:calculate("-3.3*((-1.2+0.2)*(10%(-3.2/10)))"),结果:0.2640(默认四舍五入),程序计算结果:0.2639999999999993
* @param expression 计算公式的表达式
* @param scale 用于除法计算时保留的小数位
* @param mode 选择舍入模式,默认四舍五入
* */
public static BigDecimal calculate(String expression,int scale, RoundingMode mode){
try{
if (isBlank(expression)){
throw new JSRuleException();
}
while(expression.contains(ZSSign.RIGHT_BRACKET_STR)){
StringBuilder equation = new StringBuilder(expression);
int end = equation.indexOf(ZSSign.RIGHT_BRACKET_STR);
String sub = equation.substring(0,end);
int start = sub.lastIndexOf(ZSSign.LEFT_BRACKET_STR);
expression = equation.replace(start, end+1,calculate(sub.substring(start+1), scale, mode).toPlainString()).toString();
}
LinkedList<BigDecimal> value = new LinkedList<>();
LinkedList<Character> symbol = new LinkedList<>();
StringBuilder number = new StringBuilder();
char[] cs = expression.toCharArray();
if (cs[0]=='-'){
number.append(0);
}
for (char c : cs) {
if (Character.isDigit(c) || c == '.') {
number.append(c);
} else {
if (c=='-' && number.isEmpty()){
number.append(c);
}else{
calculate(value,number.toString(),symbol,scale,mode);
number.setLength(0);
symbol.addLast(c);
}
}
}
calculate(value,number.toString(),symbol,scale,mode);
BigDecimal result = value.getFirst();
for (int i=0;i<symbol.size();i++){
result = calculate(result,symbol.get(i),value.get(i+1),scale,mode);
}
return result;
}catch (Exception e){
throw new JSRuleException(JSRuleMessage.read("illegal.expression"));
}
}
private static void calculate(LinkedList<BigDecimal> value,String number,LinkedList<Character> symbol,int scale, RoundingMode mode){
if (!symbol.isEmpty()) {
char lastSymbol = symbol.getLast();
if (lastSymbol == '+' || lastSymbol == '-') {
value.addLast(new BigDecimal(number));
} else {
value.addLast(calculate(value.removeLast(), symbol.removeLast(), new BigDecimal(number),scale,mode));
}
} else {
value.addLast(new BigDecimal(number));
}
}
public static BigDecimal calculate(BigDecimal left, char operation, BigDecimal right, int scale, RoundingMode mode){
if (operation== '+'){
return left.add(right);
}else if (operation=='-'){
return left.subtract(right);
}else if (operation=='*'){
return left.multiply(right);
}else if (operation=='/'){
return left.divide(right,scale,mode);
}else if (operation=='%'){
return left.divideAndRemainder(right)[1];
}
throw new JSRuleException(JSRuleMessage.read("unsupported.arithmetic.type"),String.valueOf(operation));
}
使用该方法可以用()括号,也可以是负数开头,只要你的表达式正确,程序能够处理的它也可以处理,唯一不同的是如果你写成这样,3.1(2+2),它也是允许的,结果为3.14