所有分类
  • 所有分类
  • 未分类

Java之BigDecimal-使用教程

简介

说明

本文用实例介绍Java中的BigDecimal。包括:用法、使用BigDecimal的场景(为什么使用BigDecimal),使用BigDecimal应该注意的问题。

使用BigDecimal的场景

典型场景是表示金额。

对于金额而言,BigDecimal是最好的选择。假如在面试中有面试官问到金额用什么类型表示,若回答BigDecimal,此题过关;若回答double、float之类的,结果很可能就是:回去等通知吧😂

在数据库中,对应的类型是decimal,例如:

BigDecimal与double

简介

特点BigDecimaldouble
精准度绝对精准小数部分极大可能损失精度
数字长度可以处理大于16位长度的数最长只能处理16为长度的数

Java中提供了java.math.BigInteger 类和 java.math.BigDecimal 类,用于处理超过16位有效位的数字。双精度浮点型变量double可以处理16位有效数。

一般情况下,对于那些不需要准确计算精度的数字,可直接用Float和Double处理,但是Double.valueOf(String) 和Float.valueOf(String)会丢失精度。所以如果需要精确计算的结果,必须使用BigDecimal类。

实例1:加法

package com.example.a;

import java.math.BigDecimal;

public class Demo {
    public static void main(String[] args) {
        double d1 = 0.3;
        double d2 = 0.2;
        System.out.println("double:\t\t 0.3 - 0.2 = " + (d1 - d2));

        float f1 = 0.3f;
        float f2 = 0.2f;
        System.out.println("float:\t\t 0.3 - 0.2 = " + (f1 - f2));

        BigDecimal bd1 = new BigDecimal("0.3");
        BigDecimal bd2 = new BigDecimal("0.2");
        System.out.println("BigDecimal:\t 0.3 - 0.2 = " + (bd1.subtract(bd2)));
    }
}

执行结果

double:		 0.3 - 0.2 = 0.09999999999999998
float:		 0.3 - 0.2 = 0.10000001
BigDecimal:	 0.3 - 0.2 = 0.1

可见:只有BigDecimal保持了精度。 

实例2:除法

package com.example.a;

import java.math.BigDecimal;
import java.math.RoundingMode;

public class Demo {
    public static void main(String[] args) {
        double d1 = 10;
        double d2 = 3;
        System.out.println("double:\t\t 10 / 3 = " + (d1 / d2));

        float f1 = 10f;
        float f2 = 3f;
        System.out.println("float:\t\t 10 / 3 = " + (f1 / f2));

        BigDecimal bd1 = new BigDecimal("10");
        BigDecimal bd2 = new BigDecimal("3");
        System.out.println("BigDecimal:\t 10 / 3 = " + (bd1.divide(bd2, 3, RoundingMode.HALF_UP)));
    }
}

结果

double:		 10 / 3 = 3.3333333333333335
float:		 10 / 3 = 3.3333333
BigDecimal:	 10 / 3 = 3.333

构造方法

本处重点介绍带小数的构造方法。其他通过int、long等进行构造的方法不赘述了。

BigDecimal BigDecimal(String s);     //推荐使用
static BigDecimal valueOf(double d); //推荐使用
BigDecimal BigDecimal(double d);     //不要使用,因为小数的精度不能保证

实例

package com.example.a;

import java.math.BigDecimal;

public class Demo {
    public static void main(String[] args) {
        BigDecimal bigDecimal1 = new BigDecimal("1.11");
        BigDecimal bigDecimal2 = BigDecimal.valueOf(1.11);
        BigDecimal bigDecimal3 = new BigDecimal(1.11);
        
        System.out.println(bigDecimal1);
        System.out.println(bigDecimal2);
        System.out.println(bigDecimal3);
    }
}

结果:

1.11
1.11
1.1100000000000000976996261670137755572795867919921875

加减乘除

方法描述
add(BigDecimal)BigDecimal对象中的值相加,然后返回这个对象
subtract(BigDecimal)BigDecimal对象中的值相减,然后返回这个对象
multiply(BigDecimal)BigDecimal对象中的值相乘,然后返回这个对象
divide(BigDecimal)BigDecimal对象中的值相除,然后返回这个对象

实例

package com.example.a;

import java.math.BigDecimal;
import java.math.RoundingMode;

public class Demo {
    public static void main(String[] args) {
        BigDecimal b1 = new BigDecimal("2.34");
        BigDecimal b2 = new BigDecimal("1.11");

        System.out.println(b1.add(b2));
        System.out.println(b1.subtract(b2));
        System.out.println(b1.multiply(b2));
        System.out.println(b1.divide(b2, 2, RoundingMode.HALF_UP));
    }
}

 结果

3.45
1.23
2.5974
2.11

注意

对于BigDecimal来说,如果结果除不尽,且没有设置进位的状态值,那就会抛出异常。

比如:

package com.example.a;

import java.math.BigDecimal;

public class Demo {
    public static void main(String[] args) {
        BigDecimal b1 = new BigDecimal("10");
        BigDecimal b2 = new BigDecimal("3");

        System.out.println(b1.divide(b2));
    }
}

执行结果

Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
	at java.math.BigDecimal.divide(BigDecimal.java:1693)
	at com.example.a.Demo.main(Demo.java:10)

所以,必须指定保留的位数,以及如何四舍五入。详见下方“四舍五入”

四舍五入(BigDecimal类型)

java.math.RoundingMode有如下模式:

模式说明示例
UP舍入远离零的舍入模式。 始终对非零舍弃部分前面的数字加 12.36 -> 2.4
DOWN接近零的舍入模式。 从不对舍弃部分前面的数字加1,即截短。2.36 -> 2.3
CEILING接近正无穷大的舍入模式。 如果 BigDecimal 为正,则舍入行为与 ROUND_UP 相同; 如果为负,则舍入行为与 ROUND_DOWN 相同。 相当于是 ROUND_UP 和 ROUND_DOWN 的合集2.36 -> 2.4 -2.36 -> -2.3
FLOOR接近负无穷大的舍入模式。 如果 BigDecimal 为正,则舍入行为与 ROUND_DOWN 相同; 如果为负,则舍入行为与 ROUND_UP 相同。 与 ROUND_CEILING 正好相反2.36 -> 2.3 -2.36 -> -2.4
HALF_UP四舍五入2.35 -> 2.4
HALF_DOWN五舍六入。2.35 -> 2.3
HALF_EVEN如果舍弃部分左边的数字为奇数,则舍入行为与 ROUND_HALF_UP 相同(四舍五入); 如果为偶数,则舍入行为与 ROUND_HALF_DOWN 相同(五舍六入)。1.15 -> 1.1,1.25 -> 1.2
UNNECESSARY断言请求的操作具有精确的结果,因此不需要舍入。 如果对获得精确结果的操作指定此舍入模式,则抛出 ArithmeticException。

也可以用BigDecimal里的如下模式常量:

  1. ROUND_UP
  2. ROUND_DOWN
  3. ROUND_CEILING
  4. ROUND_FLOOR
  5. ROUND_HALF_UP
  6. ROUND_HALF_DOWN
  7. ROUND_HALF_EVEN
  8. ROUND_UNNECESSARY

实例

package com.example.a;
 
import java.math.BigDecimal;
import java.math.RoundingMode;
 
public class Demo {
    public static void main(String[] args) {
        BigDecimal b = new BigDecimal("2.35");
        System.out.println("原始:" + b);
        System.out.println("UP:" + b.setScale(1, RoundingMode.UP));
        System.out.println("DOWN:" + b.setScale(1, RoundingMode.DOWN));
        System.out.println("CEILING:" + b.setScale(1, RoundingMode.CEILING));
        System.out.println("FLOOR:" + b.setScale(1, RoundingMode.FLOOR));
        System.out.println("HALF_UP:" + b.setScale(1, RoundingMode.HALF_UP));
        System.out.println("HALF_DOWN:" + b.setScale(1, RoundingMode.HALF_DOWN));
        System.out.println("HALF_EVEN:" + b.setScale(1, RoundingMode.HALF_EVEN));
        // 下边这个等价于:b.setScale(1);
        System.out.println("UNNECESSARY:" + b.setScale(1, RoundingMode.UNNECESSARY));
    }
}

结果

原始:2.35
UP:2.4
DOWN:2.3
CEILING:2.4
FLOOR:2.3
HALF_UP:2.4
HALF_DOWN:2.3
HALF_EVEN:2.4
Exception in thread "main" java.lang.ArithmeticException: Rounding necessary
	at java.math.BigDecimal.commonNeedIncrement(BigDecimal.java:4179)
	at java.math.BigDecimal.needIncrement(BigDecimal.java:4235)
	at java.math.BigDecimal.divideAndRound(BigDecimal.java:4143)
	at java.math.BigDecimal.setScale(BigDecimal.java:2455)
	at java.math.BigDecimal.setScale(BigDecimal.java:2389)
	at com.example.a.Demo.main(Demo.java:18)

比较大小

简介

a.compareTo(b)

结果:a > b 返回 1;a = b 返回 0;a < b 返回 -1

实例

package com.example.a;

import java.math.BigDecimal;

public class Demo {
    public static void main(String[] args) {
        BigDecimal b1 = new BigDecimal("2.34");
        BigDecimal b2 = new BigDecimal("1.11");

        System.out.println(b1.compareTo(b2));
    }
}

结果

1

格式化(保留位数等)

DecimalFormat 是 NumberFormat 的一个具体子类,用于格式化十进制数字。

符号位置说明
0数字阿拉伯数字,如果不存在则显示0
#数字阿拉伯数字,如果不存在不显示0
.数字小数分隔符或货币小数分隔符
,数字分组分隔符
E数字分隔科学计数法中的尾数和指数。在前缀或后缀中无需加引号。 
数字负号
;子模式边界分隔正数和负数子模式
%前缀或后缀乘以 100 并显示为百分数
\u2030前缀或后缀乘以 1000 并显示为千分数
¤(\u00A4)前缀或后缀货币记号,由货币符号替换。 如果两个同时出现,则用国际货币符号替换。如果出现在某个模式中,则使用货币小数分隔符,而不使用小数分隔符。
前缀或后缀用于在前缀或或后缀中为特殊字符加引号,例如 “‘#’#”将 123 格式化为 “#123″。要创建单引号本身,请连续使用两个单引号:”# o”clock”。

保留位数(String类型)

package com.example.a;

import java.math.BigDecimal;
import java.text.DecimalFormat;

public class Demo {
    public static void main(String[] args) {
        String stringNum = "12.345678";
        BigDecimal num = new BigDecimal(stringNum);

        //取所有整数部分
        System.out.println("0       :" + new DecimalFormat("0").format(num));
        System.out.println("#       :" + new DecimalFormat("#").format(num));

        //取一位整数和三位小数
        System.out.println("0.000   :" + new DecimalFormat("0.000").format(num));
        System.out.println("#.###   :" + new DecimalFormat("#.###").format(num));
        System.out.println("#.000   :" + new DecimalFormat("#.000").format(num));

        //取3位整数和三位小数,整数不足部分以0填补。
        System.out.println("000.000 :" + new DecimalFormat("000.000").format(num));
        //取3位整数和三位小数,整数不足不填补。
        System.out.println("###.### :" + new DecimalFormat("###.###").format(num));

        //以百分比方式计数,并取两位小数
        System.out.println("#.##%   :" + new DecimalFormat("#.##%").format(num));
        //以千分比方式计数,并取两位小数
        System.out.println("#.##\u2030   :" + new DecimalFormat("#.##\u2030").format(num));
    }
}

结果

0       :12
#       :12
0.000   :12.346
#.###   :12.346
#.000   :12.346
000.000 :012.346
###.### :12.346
#.##%   :1234.57%
#.##‰   :12345.68‰

科学计数法

package com.example.a;

import java.math.BigDecimal;
import java.text.DecimalFormat;

public class Demo {
    public static void main(String[] args) {
        String stringNum = "1234.567";
        BigDecimal num = new BigDecimal(stringNum);

        System.out.println(new DecimalFormat("0E0").format(num));
        System.out.println(new DecimalFormat("0E00").format(num));
        System.out.println(new DecimalFormat("#E0").format(num));
        System.out.println(new DecimalFormat("##E0").format(num));
        System.out.println(new DecimalFormat("###E0").format(num));
        System.out.println(new DecimalFormat("####E0").format(num));
        System.out.println(new DecimalFormat("#####E0").format(num));
        System.out.println(new DecimalFormat("######E0").format(num));
        System.out.println(new DecimalFormat("#######E0").format(num));
        System.out.println(new DecimalFormat("########E0").format(num));
    }
}

执行结果

1E3
1E03
.1E4
12E2
1.23E3
1235E0
1234.6E0
1234.57E0
1234.567E0
1234.567E0

分隔符和减号

分隔符

package com.example.a;

import java.math.BigDecimal;
import java.text.DecimalFormat;

public class Demo {
    public static void main(String[] args) {
        String stringNum = "1234567.89";
        BigDecimal num = new BigDecimal(stringNum);

        System.out.println(new DecimalFormat(",###").format(num));
        System.out.println(new DecimalFormat(",##").format(num));
    }
}

结果

1,234,568
1,23,45,68

减号(负数) 

-表示输出为负数, 要放在最前面

package com.example.a;

import java.math.BigDecimal;
import java.text.DecimalFormat;

public class Demo {
    public static void main(String[] args) {
        String stringNum = "1234.56789";
        BigDecimal num = new BigDecimal(stringNum);

        System.out.println(new DecimalFormat("-#.#").format(num));
    }
}

结果

-1234.56789

货币

package com.example.a;

import java.math.BigDecimal;
import java.text.DecimalFormat;

public class Demo {
    public static void main(String[] args) {
        String stringNum = "1234.56789";
        BigDecimal num = new BigDecimal(stringNum);

        System.out.println(new DecimalFormat(",000.00¤").format(num));
        System.out.println(new DecimalFormat(",000.¤00").format(num));
        System.out.println(new DecimalFormat("¤,000.00").format(num));
        System.out.println(new DecimalFormat(",00¤0.¤00").format(num));
        System.out.println(new DecimalFormat("¤,000.¤00").format(num));
        System.out.println(new DecimalFormat(",000.00¤¤").format(num));
    }
}

结果

1,234.57¥
1,234.57¥
¥1,234.57
1,234.57¥¥
¥1,234.57¥
1,234.57CNY

引号

‘ 用于引用特殊的字符,作为前缀或后缀。

package com.example.a;

import java.math.BigDecimal;
import java.text.DecimalFormat;

public class Demo {
    public static void main(String[] args) {
        String stringNum = "1234.56789";
        BigDecimal num = new BigDecimal(stringNum);

        //使用#本身作为前缀或后缀
        System.out.println(new DecimalFormat("'#'0.00").format(num));//#4.57
        //使用'本身作为前缀或后缀
        System.out.println(new DecimalFormat("''0.00").format(num));//'4.57
    }
}

结果

#1234.57
'1234.57
0

评论0

请先

显示验证码
没有账号?注册  忘记密码?

社交账号快速登录