Fel表達式使用過程中需要注意的問題


精度問題:

我們知道java中直接使用float和double參與的計算都可能會產生精度問題,比如0.1+0.3、1.0-0.9 等。所以一般財務系統,都會使用BigDecimal進行加減乘除。 在調研Fel過程中,發現Fel里進行計算都是使用浮點數加減乘除的,所以不可避免的會產生精度問題。

Case+源碼分析:

加法 Case:

FelEngine fel = new FelEngineImpl();
Object result = fel.eval("0.1+0.2");
System.out.println(result);



源碼分析:

簡單的來說,Fel首先經過詞法解析器將表達式解析成FelNode實例,FelNode包含表達式子節點(比如+號、0.1等)和表達式解析器,解析器對應的有com.greenpineyu.fel.function.operator.Add、ccom.greenpineyu.fel.function.operator.Sub、com.greenpineyu.fel.function.operator.Mul、com.greenpineyu.fel.function.operator.Div等各種解析器(詳見com.greenpineyu.fel.function.operator下的類),具體的表達式運算結果是由這些解析器計算的。具體到方法又是由com.greenpineyu.fel.function.operator.Add#call計算的。

public Object call(FelNode node, FelContext context) {
	Object returnMe = null;
	for (Iterator<FelNode> iterator = node.getChildren().iterator(); iterator.hasNext();) {
		Object child = iterator.next();
		if (child instanceof FelNode) {
			FelNode childNode = (FelNode) child;
			child = childNode.eval(context);
		}
		if (child instanceof String) {
			if (returnMe == null) {
				returnMe = child;
				continue;
			}
				returnMe = returnMe + (String) child;
		}
		if (child instanceof Number) {
			if (returnMe == null) {
				returnMe = child;
				continue;
			}
			Number value = (Number) child;
			if (returnMe instanceof Number) {
				Number r = (Number) returnMe;
				// 注意這里:是直接使用轉成double進行加減的。
				returnMe = toDouble(r) + toDouble(value);
			}else if(returnMe instanceof String){
				String r = (String) returnMe;
				returnMe=r+value;
			}
		}
	}
	if(returnMe instanceof Number){
		return NumberUtil.parseNumber(returnMe.toString());
	}
	return returnMe;
}

/**
 * 將Number轉換成double
 * @param number
 * @return
 */
public static double toDouble(Number number){
	if(number instanceof Float){
		//float轉double時,會出現精度問題。"(double)1.1f"的值類似於1.1000000476837158),
		//使用 Double.parseDouble(number.toString());則不會出現問題。
		return Double.parseDouble(number.toString());
	}
	return number.doubleValue();
}

通過上面的returnMe = toDouble(r) + toDouble(value); 代碼片段,我們就知道Fel是拿double進行加法操作的,這樣某些情況下就會產生精度問題。

其他運算操作同之。

解決辦法:
避免使用浮點數進行數值計算,可以將操作數乘以10的某個倍數,將浮點數轉成整數。至於從整數再轉成浮點數就可以使用BigDecimal了。其實,一個好的財務系統都是不會存儲和使用浮點數的,都是轉成整數,只有在進行頁面顯示的時候才處理回浮點數。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM