zoukankan      html  css  js  c++  java
  • 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了。其实,一个好的财务系统都是不会存储和使用浮点数的,都是转成整数,只有在进行页面显示的时候才处理回浮点数。

  • 相关阅读:
    11111 Generalized Matrioshkas
    Uva 442 Matrix Chain Multiplication
    Uva 10815 Andy's First Dictionary
    Uva 537 Artificial Intelligence?
    Uva 340 MasterMind Hints
    SCAU 9508 诸葛给我牌(水泥题)
    Uva 10420 List of Conquests(排序水题)
    Uva 409 Excuses, Excuses!
    10/26
    11/2
  • 原文地址:https://www.cnblogs.com/boothsun/p/7995575.html
Copyright © 2011-2022 走看看