Java高精度四則運算(無括號限制)
package cn.skyatom.common;
import java.math.BigDecimal;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 基礎四則運算
*
* @author ZWK
*/
public class Arithmetic {
private static String getUUID() {
return java.util.UUID.randomUUID().toString().replaceAll("-", "");
}
public static void main(String[] args) throws Exception {
java.util.Map<String, BigDecimal> values = new java.util.HashMap<String, BigDecimal>();
BigDecimal AAF = new BigDecimal(5.5);
BigDecimal BCCC = new BigDecimal(8);
BigDecimal QQC = new BigDecimal(-8.33);
BigDecimal DCC = new BigDecimal(2);
BigDecimal EE = new BigDecimal(23);
BigDecimal BF = new BigDecimal(2.5);
BigDecimal A1 = new BigDecimal(12);
BigDecimal A2 = new BigDecimal(4);
BigDecimal A3 = new BigDecimal(5);
BigDecimal A4 = new BigDecimal(15);
values.put("AAF", AAF);
values.put("BCCC", BCCC);
values.put("QQC", QQC);
values.put("DCC", DCC);
values.put("EE", EE);
values.put("BF", BF);
values.put("A1", A1);
values.put("A2", A2);
values.put("A3", A3);
values.put("A4", A4);
//
// values.put("B1", 4F);
// values.put("B2", 15f);
// values.put("B3", 55f);
// values.put("B4", 2f);
// values.put("B5", 5f);
String str = "AAF * BCCC + QQC /DCC-EE*BF+( A1*(A2/(A3+A4)) ) ";//5.5*8 + (-8.33)/2 - 23*2.5 + (12*(4/(5+15)) )
//String str = "B1 * B2 + B3 -B4/ B5 ";//去空白
//BigDecimal v = getArithmeticValue(str, values);
System.out.println("結果為:" + getArithmeticFloatValue(str, values));
}
/**
* 執行運算,獲取表達式的結果。float值
*
* @param str
* @param values
* @return
* @throws Exception
*/
public static float getArithmeticFloatValue(String str, java.util.Map<String, BigDecimal> values) throws Exception {
return getArithmeticValue(str, values).floatValue();
}
public static int getArithmeticIntValue(String str, java.util.Map<String, BigDecimal> values) throws Exception {
return getArithmeticValue(str, values).intValue();
}
public static long getArithmeticLongValue(String str, java.util.Map<String, BigDecimal> values) throws Exception {
return getArithmeticValue(str, values).longValue();
}
/**
* 替換括號
*
* @param str
* @param values
* @return 當所有替換完成,null,反之返回替換的字符串
*/
private static String replaceBrackets(String str, java.util.Map<String, BigDecimal> values) {
String v = "";
String tmp = null;//臨時字符串值
Pattern patt = Pattern.compile("\\(([A-Za-z0-9\\.\\*\\+\\-/]*?)\\)", Pattern.DOTALL);
Matcher mat = patt.matcher(str);
if (mat.find()) {
tmp = mat.group(1);
}
if (tmp != null) {
String uuid = getUUID();
BigDecimal value = getBasicArithmeticValue(tmp, values);
str = str.replace("(" + tmp + ")", uuid);
values.put(uuid, value);
v = str;
v = replaceBrackets(v, values);
} else {
v = str;
}
return v;
}
/**
* 執行運算,獲取表達式的結果
*
* @param str 表達式字符串
* @param values 值存儲表
* @return 返回運算值
* @throws java.lang.Exception 運算格式錯誤時,拋出異常
*/
public static BigDecimal getArithmeticValue(String str, java.util.Map<String, BigDecimal> values) throws Exception {
str = str.replaceAll("\\s*", "");//去空白
String s = replaceBrackets(str, values);
if (s != null || !s.trim().equals("")) {
str = s;
}
return getBasicArithmeticValue(str, values);
}
/**
* 基本四則運算
*
* @param str 基礎四則運算
* @param values 值存儲表
* @return
*/
private static BigDecimal getBasicArithmeticValue(String str, java.util.Map<String, BigDecimal> values) {
str = multiReg(str, values);
str = divReg(str, values);
java.util.List<Boolean> signs = getPlusReduceSign(str);//獲取符號
java.util.List<String> valuesign = getValueSign(str);//替換符號
BigDecimal v = getValue(valuesign, signs, values);
return v;
}
/**
* 獲取結果
*
* @param valuesign 值替換符號
* @param signs 符號
* @param values 值存儲表
* @return
*/
private static BigDecimal getValue(java.util.List<String> valuesign, java.util.List<Boolean> signs, java.util.Map<String, BigDecimal> values) {
BigDecimal value = values.get(valuesign.get(0));
for (int i = 0; i < signs.size(); i++) {
if (signs.get(i)) {
value = value.add(values.get(valuesign.get(i + 1)));
//value += values.get(valuesign.get(i + 1));
} else {
value = value.subtract(values.get(valuesign.get(i + 1)));
//value -= values.get(valuesign.get(i + 1));
}
}
return value;
}
/**
* 獲取替換的符號
*
* @param str 待匹配的字符串
* @return 返回 替換的值符號
*/
private static java.util.List<String> getValueSign(String str) {
java.util.List<String> list = new java.util.ArrayList<String>();
Pattern patt = Pattern.compile("([a-zA-Z0-9]*{1})", Pattern.DOTALL);
Matcher mat = patt.matcher(str);
while (mat.find()) {
if (mat.group(1).trim().equals("")) {
continue;
}
list.add(mat.group(1));
}
return list;
}
/**
* 獲取加減符號
*
* @param str 待匹配的字符串
* @return 返回 符號順序,加號為true,減號為false
*/
private static java.util.List<Boolean> getPlusReduceSign(String str) {
java.util.List<Boolean> list = new java.util.ArrayList<Boolean>();
Pattern patt = Pattern.compile("([a-zA-Z0-9]*{1}([+|-])[a-zA-Z0-9]*{1})", Pattern.DOTALL);
Matcher mat = patt.matcher(str);
while (mat.find()) {
if (mat.group(2).trim().equals("+")) {
list.add(true);
} else {
list.add(false);
}
}
return list;
}
/**
* 乘法的正則
*
* @param str 運算表達式字符串
* @param values 值存儲表
* @return 返回重構後的字符串
*/
private static String multiReg(String str, java.util.Map<String, BigDecimal> values) {
Pattern patt = Pattern.compile("([a-zA-Z0-9]*{1}\\*[a-zA-Z0-9]*{1})", Pattern.DOTALL);
Matcher mat = patt.matcher(str);
while (mat.find()) {
str = excMultiplication(str, mat.group(1), values);
}
return str;
}
/**
* 除法的正則
*
* @param str 運算表達式字符串
* @param values 值存儲表
* @return 返回重構後的字符串
*/
private static String divReg(String str, java.util.Map<String, BigDecimal> values) {
Pattern patt = Pattern.compile("([a-zA-Z0-9]*{1}\\/[a-zA-Z0-9]*{1})", Pattern.DOTALL);
Matcher mat = patt.matcher(str);
while (mat.find()) {
str = excDivsion(str, mat.group(1), values);
}
return str;
}
/**
* 計算乘法
*
* @param str 全部的運算字符串
* @param value 計算乘法的字符串
* @param map 值存儲表
* @return 返回重構後的字符串
*/
private static String excMultiplication(String str, String value, java.util.Map<String, BigDecimal> map) {
String vs[] = value.split("\\*");
BigDecimal v1 = map.get(vs[0]);
BigDecimal v2 = map.get(vs[1]);
BigDecimal x = v1.multiply(v2);
map.remove(vs[0]);
map.remove(vs[1]);
String uuid = getUUID();
map.put(uuid, x);
str = str.replace(value, uuid);
return str;
}
/**
* 計算出發
*
* @param str 全部的運算字符串
* @param value 計算乘法的字符串
* @param map 值存儲表
* @return 返回重構後的字符串
*/
private static String excDivsion(String str, String value, java.util.Map<String, BigDecimal> map) {
String vs[] = value.split("\\/");
BigDecimal v1 = map.get(vs[0]);
BigDecimal v2 = map.get(vs[1]);
BigDecimal x = v1.divide(v2);
map.remove(vs[0]);
map.remove(vs[1]);
String uuid = getUUID();
map.put(uuid, x);
str = str.replace(value, uuid);
return str;
}
}
最近在開發公司的績效系統,老板又很摳門,不想購買市面上的績效系統。。於是乎,苦逼的我又開始了苦逼的編程……
系統裡面涉及到報表,而各員工間的績效以及實發工資的算法每隔一段時間就會更新,所以不可能做到將算法寫進源碼編譯,需要報表公式進行計算(當然,報表公式系統需要另行開發,這裡不描述)。
以上是JAVA代碼,其思想是將公式字中的參數當作字符串來看待,並不是當作純粹的數字。每次運算時,只進行二元運算,運算之後將數據保存,再刪除原數據表中的數據。如此循環,無需考慮括號的限制了。
在實現代碼時,考慮了直接使用float或者是double類型,但是在最終顯示結果是,兩個類型精度都會產生精度上的丟失,所以最終考慮使用BigDecimal類型,也算是做長期運行考慮吧。
其中有很多需要優化的地方,敢興趣的朋友可以留言交流……