精度问题:

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

Fel表达式使用过程中需要注意的问题的更多相关文章

  1. lua解析脚本过程中的关键数据结构介绍

    在这一篇文章中我先来介绍一下lua解析一个脚本文件时要用到的一些关键的数据结构,为将来的一系列代码分析打下一个良好的基础.在整个过程中,比较重要的几个源码文件分别是:llex.h,lparse.h.l ...

  2. ios逆向过程中lldb调试技巧

    在ios逆向过程中,善于运用lldb,会给逆向带来很大的方便 一般的命令: 1.image list -o -f  看看各个模块在内存中的基址 2.register read r0  读取寄存器r0的 ...

  3. react使用过程中常见问题

    目录 一.减小输入字符数 二.用props.children来引用位于前置标签和后置标签之间的内容 三.创建组件两条主要的途径 四.JSX属性采用驼峰式的大小写规则(即‘onClick’而非‘oncl ...

  4. <转>lua解析脚本过程中的关键数据结构介绍

    在这一篇文章中我先来介绍一下lua解析一个脚本文件时要用到的一些关键的数据结构,为将来的一系列代码分析打下一个良好的基础.在整个过程中,比较重要的几个源码文件分别是:llex.h,lparse.h.l ...

  5. Sybase IQ使用过程中注意事项

    Sybase IQ使用过程中注意事项 1,字母大小写比对不敏感,也就是在值比对判断时大小写字母都一样; 2,等值,或<>判断,系统默认对等式两边比对值去右边空格再进行比较: 3,GROUP ...

  6. 计算后缀表达式的过程(C#)

    计算后缀表达式的过程是一个很好玩的过程,而且很简单哦!这里呢,有个计算的技巧,就是:遇到数字直接入栈,遇到运算符就计算! 后缀表达式也叫逆波兰表达式,求值过程可以用到栈来辅助存储: 假定待求值的后缀表 ...

  7. this在方法赋值过程中无法保持(隐式丢失)

    在看<高级程序设计>(我的红宝书) P.183页时遇到下面一个问题 var name = "77"; var obj = { name: "88", ...

  8. 转:Oracle中SQL语句执行过程中

    Oracle中SQL语句执行过程中,Oracle内部解析原理如下: 1.当一用户第一次提交一个SQL表达式时,Oracle会将这SQL进行Hard parse,这过程有点像程序编译,检查语法.表名.字 ...

  9. springfox-swagger原理解析与使用过程中遇到的坑

    swagger简介 swagger确实是个好东西,可以跟据业务代码自动生成相关的api接口文档,尤其用于restful风格中的项目,开发人员几乎可以不用专门去维护rest api,这个框架可以自动为你 ...

随机推荐

  1. [Leetcode] valid sudoku 有效数独

    Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules. The Sudoku board could be ...

  2. [Leetcode] Construct binary tree from inorder and postorder travesal 利用中序和后续遍历构造二叉树

    Given inorder and postorder traversal of a tree, construct the binary tree. Note:  You may assume th ...

  3. Codeforces 864E Fire(背包DP)

    背包DP,决策的时候记一下 jc[i][j]=1 表示第i个物品容量为j的时候要选,输出方案的时候倒推就好了 #include<iostream> #include<cstdlib& ...

  4. 会话技术: Cookie 和 Session

    会话技术 会话技术:从浏览器开始访问服务器,到关闭浏览器,这期间发生了许多次请求和响应,这个过程就叫做一次会话. Cookie 和 Session 都是处理会话技术的两种具体实现,Cookie将数据保 ...

  5. ExecuteNonQuery,ExecuteReader,ExecuteScalar 区别

    ExecuteNonQuery方法 :执行非查询SQL操作,包括增insert.删delete.改update ExcuteReader方法 :执行查询,返回DataReader,通过DataRead ...

  6. 获取Web.Config中节点的值

    读取webconfig里面的appSetting和connectionString <appSettings> <add key="SiteURL" value= ...

  7. sql 建表以及查询---复杂查询之成绩排名

    废话不说,直接建表 1.表Player USE T4st -- 设置当前数据库为T4st,以便访问sysobjects IF EXISTS(SELECT * FROM sysobjects WHERE ...

  8. 希尔排序Shell sort

    希尔排序Shell Sort是基于插入排序的一种改进,同样分成两部分, 第一部分,希尔排序介绍 第二部分,如何选取关键字,选取关键字是希尔排序的关键 第一块希尔排序介绍 准备待排数组[6 2 4 1 ...

  9. django中处理表单的经典流程

    def form_process_view(request): if request.method == 'POST': # 请求为 POST,利用用户提交的数据构造一个绑定了数据的表单 form = ...

  10. Chrome浏览器启动页被360导航篡改解决方法

    右键Chrome浏览器快捷方式,选择“属性”,在“目标”的结尾处有添加的网址,删了即可. 2 如果在结尾处没有任何网址,可以添加“ -nohome”,这样下次启动时,就会打开一个空白页,也就不会打开被 ...