likes
comments
collection
share

BigDecimal 简单使用

作者站长头像
站长
· 阅读数 1

为什么使用BigDecimal

  • 1.float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果
  • 2.作为移动端,经常调用后端接口,返回参数类型由后端决定。当我们使用double去接收并使用可能产生以下问题。
    public void dfFormat() {
        double number =1000000000000.0;
        //变成了科学计数法
        log("number直接打印->"+number);
        log("numberString.valueOf->"+String.valueOf(number));
    }

输出结果

number直接打印->1.0E12,这里直接变成了科学计数法。

numberString.valueOf->1.0E12,转String也是科学计数法。

解决方案

  • 1、如果仅展示不需要后续做处理(保留小数位、加减乘除等),可以让后端返回String类型。

  • 2、使用BigDecimal

    • 1.直接使用BigDecimal替代double接收数据。
    • 2.将double转成BigDecimal。
    public void dfFormat() {
        double number =1000000000000.0;
        //toPlainString
        log("numberDecimal-> "+BigDecimal.valueOf(number).toPlainString());
    }

打印结果:

numberDecimal-> 1000000000000,从结果可以看出,数据显示正常了。

double转BigDecimal,使用的是BigDecimal.valueOf(number) ,而不是 new BigDecimal(number)

BigDecimal转成String,使用的是toPlainString() ,而不是toString()

构造方法

BigDecimal 简单使用

BigDecimal提供的比较常用的是下面几种方法:

  • 1.public BigDecimal(String val):将 String 表示形式转换成 BigDecimal(推荐使用)
  • 2.public BigDecimal(int val):将 int 表示形式转换成 BigDecimal
  • 3.public BigDecimal(double val):将 double 表示形式转换为 BigDecimal(不推荐使用)
    public void dfFormat() {
        BigDecimal a = new BigDecimal("10");
        BigDecimal b = new BigDecimal(5);
        BigDecimal k = new BigDecimal(-16.0034);
    }

输出结果

 a-> 10
 b-> 5
 k-> -16.00339999999999918145476840436458587646484375

原因:float和double类型的主要设计目标是为了科学计算和工程计算。他们执行二进制浮点运算,这是为了在广域数值范围上提供较为精确的快速近似计算而精心设计的。然而,它们没有提供完全精确的结果,所以不应该用于要求精确结果的场合。

类型转换

注意:double转BigDecimal使用new BigDecimal()的方式会造成精度丢失。怎么解决?看下面的类型转换。

类型转换

double 转 BigDecimal

精度丢失解决方案:

  • 1:new BigDecimal(String.valueOf(number2));
  • 2:BigDecimal.valueOf(number2);
    public void dfFormat() {
  double number2 =-16.0034;
        log("number2直接打印->"+number2);
        //double转BigDecimal导致丢失精度
        log("number2Decimal-> "+new BigDecimal(number2).toPlainString());

        //精度丢失解决方案:
        log("精度丢失String.valueOf-> "+new BigDecimal(String.valueOf(number2)).toPlainString());
        log("精度丢失BigDecimal.valueOf-> "+BigDecimal.valueOf(number2).toPlainString());
    }

输出结果

精度丢失String.valueOf-> -16.0034
精度丢失BigDecimal.valueOf-> -16.0034

BigDecimal 转 String

    public void dfFormat() {
        double number =1000000000000.0;
        //toString和toPlainString
        log("toString-> "+new BigDecimal(String.valueOf(number)).toString());
        log("toString-> "+BigDecimal.valueOf(number).toString());
        log("toPlainString-> "+ new BigDecimal(String.valueOf(number)).toPlainString());
        log("toPlainString-> "+BigDecimal.valueOf(number).toPlainString());

两种方式对比toString和toPlainString的区别,打印结果:

toString-> 1.0E+12
toString-> 1.0E+12
toPlainString-> 1000000000000
toPlainString-> 1000000000000

注意:很多人肯定习惯使用toString()了,但在BigDecimal这里就需要注意了,要使用toPlainString()。

BigDecimal 转 double/int/long等

BigDecimal 简单使用

        //如:BigDecimal转double
        BigDecimal b = new BigDecimal(5);
        //doubleValue-> 5.0
        log("doubleValue-> "+b.doubleValue());

加减乘除取余

BigDecimal 提供的方法:

    public BigDecimal add(BigDecimal augend) {}//加
    public BigDecimal subtract(BigDecimal subtrahend) {}//减
    public BigDecimal multiply(BigDecimal multiplicand) {}//乘
    public BigDecimal divide(BigDecimal divisor) {}//除(不推荐使用)
    public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) {}//除(推荐使用)
    public BigDecimal[] divideAndRemainder(BigDecimal divisor) {}//取余
        //加减乘除
        BigDecimal a = new BigDecimal("10");
        BigDecimal b = new BigDecimal(5);
        log("10 + 5-> "+a.add(b).toPlainString());
        log("10 - 5-> "+a.subtract(b).toPlainString());
        log("10 * 5-> "+a.multiply(b).toPlainString());
        log("10 / 5-> "+a.divide(b).toPlainString());
        
        BigDecimal e = BigDecimal.valueOf(5.34178);
        BigDecimal f = BigDecimal.valueOf(3.12456);
        //e.divide(f)直接报错
//        e("e / f-> "+e.divide(f).toPlainString());
        log("e / f-> "+e.divide(f,3,BigDecimal.ROUND_HALF_UP).toPlainString());
        //取余
        BigDecimal []h = e.divideAndRemainder(f);
        log("e / f 取余 整-> "+h[0]+"余->"+h[1]);
        BigDecimal []j = a.divideAndRemainder(b);
        log("10 / 5 取余 整-> "+j[0]+"余->"+j[1]);

输出结果

10 + 5-> 15
10 - 5-> 5
10 * 5-> 50
10 / 5-> 2
e / f-> 1.710
e / f 取余 整-> 1余->2.21722
10 / 5 取余 整-> 2余->0

注意:

  • 1.为什么不推荐使用divide(BigDecimal divisor)?当 BigDecimal除法可能出现不能整除的情况,就会发送运行时异常:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result。
  • 2.跟String一样, BigDecimal页是不可变的,在进行每一步运算时,都会产生一个新的对象。

divide

    e.divide(f,3,BigDecimal.ROUND_HALF_UP)

参数解释:

  • divisor:除数
  • scale:表示小数点保留位数
  • roundingMode:表示舍入模式

舍入模式

我们一般舍入模式采取的是ROUND_HALF_UP,默认的也是ROUND_HALF_UP

  • ROUND_CEILING //向正无穷方向舍入
  • ROUND_UP //不管保留数字后面是大是小 (0 除外) 都会进1
  • ROUND_DOWN //舍去制,截断操作,后面所有数字直接去除。结果会向原点方向对齐
  • ROUND_FLOOR //向负无穷方向舍入
  • ROUND_HALF_EVEN //向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,如果保留位数是奇数,使用ROUND_HALF_UP,如果是偶数,使用ROUND_HALF_DOWN
  • ROUND_HALF_UP //向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向上舍入, 3.15保留一位小数结果为3.2,传说中的四舍五入。
  • ROUND_HALF_DOWN //向(距离)最近的一边舍入,除非两边(的距离)是相等,如果是这样,向下舍入, 例如3.15 保留一位小数结果为3.1
  • ROUND_UNNECESSARY //计算结果是精确的,不需要舍入模式

比较大小

        //比较大小
        BigDecimal u = new BigDecimal("1");
        //BigDecimal.ZERO==0;
        if(u.compareTo(BigDecimal.ZERO)==-1){
            log("u 小于 0");
        }
        if(u.compareTo(BigDecimal.ZERO)==0){
            log("u 等于 0");
        }
        if(u.compareTo(BigDecimal.ZERO)==1){
            log("u 大于 0");
        }
        BigDecimal um = new BigDecimal("-1");
        BigDecimal uzero = new BigDecimal("0");
        if(um.compareTo(BigDecimal.ZERO)==-1){
            log("um 小于 0");
        }
        if(uzero.compareTo(BigDecimal.ZERO)==0){
            log("uzero 等于 0");
        }

输出结果

 u 大于 0
 um 小于 0
 uzero 等于 0

BigDecimal 提供了三个常量分别为0,1,10。使用方法如上面的BigDecimal.ZERO(0)。

    //0
    public static final BigDecimal ZERO = new BigDecimal(0, 0);
    //1
    public static final BigDecimal ONE = new BigDecimal(1, 0);
    //10
    public static final BigDecimal TEN = new BigDecimal(10, 0);

格式化(DecimalFormat)

比如我们常用的保留小数位、补零、去零。


        DecimalFormat df2 = new DecimalFormat("######0.##");
        DecimalFormat df3 = new DecimalFormat("######0.###");
        //保留2位小数,不补零
        log(df2.format(3.1415927));//3.14
        log(df2.format(3.1));//3.14
        //保留3位小数,不补零
        log(df3.format(3.1415927));//3.142,默认四舍五入
        log(df2.format(3.1));//3.14
        DecimalFormat df22 = new DecimalFormat("######0.00");
        DecimalFormat df32 = new DecimalFormat("######0.000");
        //保留2位小数,补零
        log(df22.format(3.2));//3.20
        //保留3位小数,补零
        log(df32.format(3.2));//3.200
        //去掉BigDecimal后无用的零
        BigDecimal g = new BigDecimal("0.1000");
        log(g.stripTrailingZeros().toPlainString());//0.1
        
        double pi = 3.1415927;
        System.out.print(pi);
        //取一位整数
        log(new DecimalFormat("0").format(pi));//3
        //取一位整数和两位小数
        log(new DecimalFormat("0.00").format(pi));//3.14
        //取两位整数和三位小数,整数不足部分以0填补。
        log(new DecimalFormat("00.000").format(pi));// 03.142
        //取所有整数部分
        log(new DecimalFormat("#").format(pi));//3
        //以百分比方式计数,并取两位小数
        log(new DecimalFormat("#.##%").format(pi));//314.16%
        long c = 299792458;//光速
        //每三位以逗号进行分隔。
        log(new DecimalFormat(",###").format(c)); //299,792,458
        // 将格式嵌入文本光速大小为每秒299,792,458米。
        log(new DecimalFormat("光速大小为每秒,###米。").format(c)); 

小结

  • 1.double解决不了的时候使用BigDecimal。
  • 2.尽量使用参数类型为String的构造函数。
  • 3.double转BigDecimal时,不要使用new BigDecimal()。
  • 4.BigDecimal转String时,使用toPlainString()而不是toString()。
  • 5.除法运算,需要考虑除不尽的情况。