一. 前言
- 最近有一个新的需求,使用固定公式,代入值,计算结果。例如(3*B-99)*5,其中B预期设想是使用反射获取。
二. 使用BigDecimal的原因。
- float : 浮点型,含字节数为4,32bit,数值范围为-3.4E38~3.4E38(7个有效位)
- double:双精度实型,含字节数为8,64bit数值范围-1.7E308~1.7E308(15个有效位)
- decimal:数字型,128bit,不存在精度损失,常用于银行帐目计算。(28个有效位)
三. 具体代码
package com.demo.am.util;
import org.apache.commons.lang3.StringUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
/**
* @author l
* Note
* 1.未解析公式中出现的百分比,如 A*B% 需要转换写法,变为 A*B/100
* 2.本工具只有简单的加减乘除,没有开发更复杂的函数操作
*/
public class Calculator {
/**
* 变量集合
**/
private static final List<String> varList = new LinkedList<>();
/**
* 变量和值的key-value
**/
private static final Map<String, BigDecimal> varListWithValue = new HashMap<>();
public static void mockCalculate() {
Scanner sc = new Scanner(System.in);
System.out.print("请输入要计算的公式: ");
String formula = sc.next();
String str = replaceBlankSpace(formula);
// 赋值变量列表
assignmentFinalVarList(str);
// 赋值对应值
varList.forEach(s -> {
System.out.print("请输入" + s + "的值: ");
BigDecimal decimal = sc.nextBigDecimal();
varListWithValue.put(s, decimal);
});
// 代回公式进行替换
String newFormula = replaceFormula(str);
// 进行计算
BigDecimal decimal = calculate(newFormula);
System.out.println("计算结果:" + decimal);
}
/**
* Calculate result.
*
* @param formula formula
* @return decimal
*/
public static BigDecimal calculate(String formula) {
while (isExistBracket(formula)) {
String resultFormula = null;
// 找最内层的括号里面的内容出来(含括号)
String calcString = findBracket(formula);
if (StringUtils.isNotBlank(calcString)) {
BigDecimal result = calculateCourse(calcString);
// 替换括号的内容(含括号)
resultFormula = formula.replace(calcString, String.valueOf(result));
}
if (StringUtils.isNotBlank(resultFormula)) {
formula = resultFormula;
}
}
// 最后得到普通顺序无括号公式:
// 公式可能还会存在括号,如(A+B*C),其不能算作错误公式
// 最后一次计算:
return calculateCourse(formula).setScale(2, RoundingMode.HALF_UP);
}
private static String findBracket(String formula) {
// 寻找最后一个左括号里面到第一个右括号里面的内容
char[] formulaArray = formula.toCharArray();
for (int i = 0; i < formulaArray.length; i++) {
if (formulaArray[i] == ')') {
int begin = 0;
for (int j = i; j >= 0