Java中double类型精度丢失问题

  首先,之所以double浮点数类型中会出现精度不准确这个问题,是因为浮点数在计算机的存储方法导致的,要想具体了解的话,可以查看《计算机组成原理》这本书,里面有比较详尽的解答。
  接下来由一道题来引出接下来要讲的这个问题:
  对任意给定的一个double型的数(小数点位数大于2),要求最后输出的数,精确到小数点后两位,采用四舍五入计算。
  最开始的时候我是想到一个方法,是把整数部分和小数部分分开,在小数部分直接计算四舍五入,最后输出的时候再重新加起来就好。下面是我当时写的代码:

    public static double TwoDecimalPlaces(double number){
        int z = (int)number;
        double x = number - z;
        if((int)(x * 1000) % 10 >= 5){
            return z + (double)((int)(x * 100) + 1)) / 100;
        } 
        return z + (double)((int)(x * 100))) / 100;
    }

  这里面首先我没有考虑到整型溢出的问题,因为一大于Integer.MAX_VALUE或者小于Integer.MIN_VALUE,就会发生溢出。果然一个大数进去就出错了(175464815.2154354)。再者,我发现里面还有另外的错误,就是比如用2.675这个数的话,在到算小数部分的时候,x本应该是0.675的,但是最后却是变成了0.6749999999999998了,但是我也是Debug才发现的这个问题。之后上网查了之后才发现,原来是double类型精度丢失造成的。

  发现之后,我就用了BigDecimal来改,但是在这里面又发现了一个问题,就是BigDecimal的构造函数中用Double的话,也是有可能会发生精度丢失的问题的。

    public static double TwoDecimalPlaces(double number)
    {
        BigDecimal b = new BigDecimal(number);
        return b.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();
    }
  上面代码很多测试用例都是能通过的,就是在只有三个小数位的数上精确出错,比如还是这个数2.675,本来精确之后,答案应该是2.68的,但是我上面的代码运行之后答案却是2.67。这就让我很郁闷了,到底是hi错在哪里呢?我在网上找了好久,最后还是在API中找到了答案。
  API中是这样解释的:
注: 
  1、此构造方法的结果有一定的不可预知性。有人可能认为在 Java 中写入 new BigDecimal(0.1) 所创建的 BigDecimal 正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于 0.1000000000000000055511151231257827021181583404541015625。这是因为 0.1 无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)。这样,传入 到构造方法的值不会正好等于 0.1(虽然表面上等于该值)。 
  2、另一方面,String 构造方法是完全可预知的:写入 new BigDecimal("0.1") 将创建一个 BigDecimal,它正好 等于预期的 0.1。因此,比较而言,通常建议优先使用 String 构造方法。 
  3、当 double 必须用作 BigDecimal 的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用 Double.toString(double) 方法,然后使用 BigDecimal(String) 构造方法,将 double 转换为 String。要获取该结果,请使用 static valueOf(double) 方法。 
    public static double TwoDecimalPlaces(double number)
    {
        BigDecimal b = new BigDecimal(Double.toString(number));
        return b.setScale(2,BigDecimal.ROUND_HALF_UP).doubleValue();
    }

  最后,我还是跟API说的一样,将double转换为String之后在构造BigDecimal函数,这样就应该没错了吧。(由于错了太多次,不太敢相信这里边还有没有其他的什么精度丢失的情况。)

附:
  其实,还有其他的方法来解决保留小数位数的,下面介绍几种:

1、

    java.text.DecimalFormat df = new   java.text.DecimalFormat(”#.00″);
    df.format(你要格式化的数字);

2、

    double d = 3.1415926;
    String result = String .format(”%.2f”);

3、(在Javabean中)

    write name="entity" property="dkhAFSumPl"  format="0.00" />

4、(其中,digits 显示的数字位数
为格式化对象设定小数点后的显示的最多位,显示的最后位是舍入的)

    double x=23.5455; 
    NumberFormat ddf1=NumberFormat.getNumberInstance() ; 
    ddf1.setMaximumFractionDigits(2); 
    String s= ddf1.format(x) ; 
    System.out.print(s); 
目前了解的就这几种方法,要是再想到新的解法会再来更新此文章。

你可能感兴趣的:(Java)