编程规范之浮点数的比较

一、问题

项目在 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。

我们可以在工具类中,实现一个浮点数比较的方法:

static 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 
}

大体是这个意思把。以上是c语法,也不甚很严谨,没测试。

浮点数的比较方法,百度上一大堆,大家可以搜一下。这里就不详细讲解了。

原文地址:https://www.cnblogs.com/senline/p/foat_number_compare.html