一、问题
项目在组 UAT,客户报了一个错误,说在提交一个单子的时候,系统报“主表总金额与明细汇总总金额不相等”,提交失败。
二、分析原因
开发上死活查不出原因,也不能再现错误,用用户的账号提交报错的单子也没问题,提交成功。说用户是不是看花眼了等等。
我帮他分析原因。
我:这个检查是前台校验,还是后台校验
开发:后台校验
我:是不是提交时,没有保存数据,修改前确实不相等,导致检查不通过
开发:不会,单据投的金额是根据明细行自动汇总计算的,并且提交前强制保存数据。
我让测试试图再现这个问题。
果然,测试竟然再现了这个问题,从界面上看,金额事项等的。
我:你把校验逻辑再检查一下
开发:检查了好几遍了,没问题
我:把校验逻辑发给我
开发:
double pBckpje = 0.0; double pSumje = 0.0; //取主表总金额 //-----省略 //取明细汇总金额 //-----省略 if (CommonUtils.round(pBckpje, 2) != CommonUtils.round(pSumje, 2)) { throw new Exception("主表总金额与明细汇总总金额不相等"); }
浮点数这样比较???
因为这段代码是为老程序员写的,我一点没有怀疑是浮点数比较的问题。所以我不大相信,因为这里用到了一个工具类的round方法,也许里边有什么玄机。
我:把这个round 代码贴出来:
开发:
public static double round(double amt, int scale) { BigDecimal bigDecimal = new BigDecimal(String.valueOf(amt)); return bigDecimal.setScale(scale,BigDecimal.ROUND_HALF_UP).doubleValue(); }
看到这里我有点上火了。
这个方法用bigdecimal 做了 round,然后有转换成了double返回,对后边的比较一点好处没有。
三、如何比较浮点数
大家都知道,在二级制世界中,浮点数是不精确的,因此直接用 <>=!=比较浮点数大小是很危险的,而且结果也是不确定的。
不考虑精度来比较浮点数大小是耍流氓。我之前的做法是基于精度对浮点数进行比较,比如,精度为2,如果 abs(d1-d2)<0.01,就说明 d1=d2。否则就是 d1>d2。
可以在工具类中,实现一个浮点数比较的方法:
int floatCompare( double d1, double d2, int scale ){ if (Math.Abs( d1 - d2 ) < 1/10^scale ) return 0 ; //d1=d2 else if ( d1 - d2 >= 1/10^scale )return 1 ; // d1>d2 return -1 ; //d1<d2 }
大体是这个意思把。java 语法不熟悉,类语言写的,也不甚很严谨,没测试。
百度上一大堆,大家可以搜一下。这里就不详细讲解了。