黑马程序员技术交流社区

标题: 商业计算中Java高精度计算BigDecimal类 [打印本页]

作者: 于世德    时间: 2013-7-25 13:40
标题: 商业计算中Java高精度计算BigDecimal类

《Effective Java》 第48条:如果需要精确的答案,请避免使用float和double。

public class Test{
    public static void main(String args[]){
        System.out.println(0.05+0.01);
        System.out.println(1.0-0.42);
        System.out.println(4.015*100);
        System.out.println(123.3/100);
    }
};
你没有看错!结果确实是
0.060000000000000005
0.5800000000000001
401.49999999999994
1.2329999999999999


四舍五入
我们的第一个反应是做四舍五入。Math类中的round方法不能设置保留几位小数,我们只能象这样(保留两位):
public double round(double value){
    return Math.round(value*100)/100.0;
}
非常不幸,上面的代码并不能正常工作,给这个方法传入4.015它将返回4.01而不是4.02,如我们在上面看到的
4.015*100=401.49999999999994

BigDecimal
在《Effective Java》这本书中也提到这个原则,float和double只能用来做科学计算或者是工程计算,在商业计算中我们要用java.math.BigDecimal。
BigDecimal(String val)
          Translates the String repre sentation of a BigDecimal into a BigDecimal.

解决方案
工具类Arith

public static double add(double v1,double v2)
public static double sub(double v1,double v2)
public static double mul(double v1,double v2)
public static double div(double v1,double v2)
public static double div(double v1,double v2,int scale)
public static double round(double v,int scale)

import java.math.BigDecimal;
  /**
  * 由于Java的简单类型不能够精确的对浮点数进行运算,这个工具类提供精
  * 确的浮点数运算,包括加减乘除和四舍五入。
  */
  public class Arith{
      //默认除法运算精度
      private static final int DEF_DIV_SCALE = 10;
      //这个类不能实例化
     private Arith(){
     }

     /**
      * 提供精确的加法运算。
      * @param v1 被加数
      * @param v2 加数
      * @return 两个参数的和
      */
     public static double add(double v1,double v2){
         BigDecimal b1 = new BigDecimal(Double.toString(v1));
         BigDecimal b2 = new BigDecimal(Double.toString(v2));
         return b1.add(b2).doubleValue();
     }

     /**
      * 提供精确的减法运算。
      * @param v1 被减数
      * @param v2 减数
      * @return 两个参数的差
      */
     public static double sub(double v1,double v2){
         BigDecimal b1 = new BigDecimal(Double.toString(v1));
         BigDecimal b2 = new BigDecimal(Double.toString(v2));
         return b1.subtract(b2).doubleValue();
     }

     /**
      * 提供精确的乘法运算。
      * @param v1 被乘数
      * @param v2 乘数
      * @return 两个参数的积
      */
     public static double mul(double v1,double v2){
         BigDecimal b1 = new BigDecimal(Double.toString(v1));
         BigDecimal b2 = new BigDecimal(Double.toString(v2));
         return b1.multiply(b2).doubleValue();
     }

     /**
      * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到
      * 小数点以后10位,以后的数字四舍五入。
      * @param v1 被除数
      * @param v2 除数
      * @return 两个参数的商
      */
     public static double div(double v1,double v2){
         return div(v1,v2,DEF_DIV_SCALE);
     }

     /**
      * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指
      * 定精度,以后的数字四舍五入。
      * @param v1 被除数
      * @param v2 除数
      * @param scale 表示表示需要精确到小数点以后几位。
      * @return 两个参数的商
      */
     public static double div(double v1,double v2,int scale){
         if(scale<0){
             throw new IllegalArgumentException(
                 "The scale must be a positive integer or zero");
         }
         BigDecimal b1 = new BigDecimal(Double.toString(v1));
         BigDecimal b2 = new BigDecimal(Double.toString(v2));
         return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
     }

     /**
      * 提供精确的小数位四舍五入处理。
      * @param v 需要四舍五入的数字
      * @param scale 小数点后保留几位
      * @return 四舍五入后的结果
      */
     public static double round(double v,int scale){
         if(scale<0){
             throw new IllegalArgumentException(
                 "The scale must be a positive integer or zero");
         }
         BigDecimal b = new BigDecimal(Double.toString(v));
         BigDecimal one = new BigDecimal("1");
         return b.divide(one,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
     }
};

创建一个测试类:

public class Test {

       public static void main(String[] args) {
              //直接使用浮点数进行计算,得到的结果是有问题的
              System.out.println(0.01+0.05);
              //使用了BigDecimal类进行计算后,可以做到精确计算
              System.out.println(Arith.add(0.05, 0.01));
       }
}
控制台输出:
0.060000000000000005
0.06

BigDecimal精度计算的舍入模式

ROUND_CEILING. 类 java.math.BigDecimal 中的静态变量如果 BigDecimal 为正,则作 ROUND_UP 操作;如果为负,则作 ROUND_DOWN 操作。

ROUND_DOWN. 类 java.math.BigDecimal 中的静态变量从不在舍弃(即截断)的小数之前增加数字。

ROUND_FLOOR. 类 java.math.BigDecimal 中的静态变量如果 BigDecimal 为正,则作 ROUND_UP ;如果为负,则作 ROUND_DOWN 。

ROUND_HALF_DOWN.类 java.math.BigDecimal 中的静态变量若舍弃部分> .5 ,则作 ROUND_DOWN;否则,作 ROUND_DOWN 操作

ROUND_HALF_EVEN. 类 java.math.BigDecimal 中的静态变量如果舍弃部分左边的数字为奇数,则作 ROUND_HALF_UP 操作;如果它为偶数,则作 ROUND_HALF_DOWN 操作

ROUND_HALF_UP.类 java.math.BigDecimal 中的静态变量若舍弃部分>=.5,则作 ROUND_UP ;否则,则作

ROUND_DOWNROUND_UNNECESSARY. 类 java.math.BigDecimal 中的静态变量该 “伪舍入模式”实际是要求操作有一个精确结果,因此不需要舍入。

ROUND_UP. 类 java.math.BigDecimal 中的静态变量总是在非零的舍弃小数(即截断)之前增加数字。












欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2