Bootstrap

java中的BigDecimal使用

1、什么是BigDecimal?

BigDecimal属于java.math包,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数。在实际应用中,需要对更大或者更小的数进行运算和处理。

float和double只能用来做科学计算或者是工程计算,在商业计算中(如金融行业)要用java.math.BigDecimal。

BigDecimal所创建的是对象,我们不能使用传统的+、-、*、/等算术运算符直接对其对象进行数学运算,而必须调用其相对应的方法。方法中的参数也必须是BigDecimal的对象。构造器是类的特殊方法,专门用来创建对象,特别是带有参数的对象。

2、为什么使用BigDecimal?

我们看如下代码:

public static void main(String[] args) throws Exception {
        System.out.println(0.2 + 0.1);
        System.out.println(0.3 - 0.1);
        System.out.println(0.2 * 0.1);
        System.out.println(0.3 / 0.1);
    }

打印结果:

在这里插入图片描述
为何会这样?

原因:不论是float 还是double都是浮点数,而计算机是二进制的,浮点数会失去一定的精确度。

根本原因:十进制值通常没有完全相同的二进制表示形式;十进制数的二进制表示形式可能不精确。只能无限接近于那个值。

但是,在项目中,我们不可能让这种情况出现,特别是金融项目,因为涉及金额的计算都必须十分精确,否则,如果你的微信账户余额显示193.99999999999998,那是一种怎么样的体验?

3、如何使用BigDecimal?

(1)BigDecimal初始化赋值

方法类型描述
public BigDecimal(int val)构造函数int类型的值生成BigDecimal对象
public BigDecimal(long val)构造函数long类型的值生成BigDecimal对象
public BigDecimal(String val)静态方法String类型的值转换为BigDecimal类型
public static BigDecimal valueOf(double val)静态方法double类型的值转换为BigDecimal类型
public static BigDecimal valueOf(long val)静态方法long类型(包含int类型)的值转换为BigDecimal类型

代码示例:

 		BigDecimal b = new BigDecimal("25");
        BigDecimal c = new BigDecimal(12);

        System.out.println(b);
        System.out.println(c);

打印结果:

在这里插入图片描述

注意:不建议使用public BigDecimal(double val)方式初始化值

如下代码:

		BigDecimal c = new BigDecimal(12.3);
        System.out.println(c);

打印结果:

在这里插入图片描述
如图,也会出现计算的错误。

通常建议优先使用String构造方法。当double必须用作BigDecimal的源时,请使用Double.toString(double)转成String,然后使用String构造方法,或使用BigDecimal的静态方法valueOf:

		BigDecimal b = new BigDecimal("12.3");
        BigDecimal c = BigDecimal.valueOf(7.34325);
        BigDecimal d = new BigDecimal(Double.toString(7.34325));
        System.out.println(b);
        System.out.println(c);
        System.out.println(d);

打印结果:

在这里插入图片描述

(2)加减乘除运算

运算法则对应方法
加法public BigDecimal add(BigDecimal value)
减法public BigDecimal subtract(BigDecimal value)
乘法public BigDecimal multiply(BigDecimal value)
除法public BigDecimal divide(BigDecimal value)

代码示例:

public static void main(String[] args) throws Exception {
        System.out.println("计算加法: " + BigDecimal.valueOf(1.9).add(BigDecimal.valueOf(0.2)));
        System.out.println("计算减法: " + BigDecimal.valueOf(1.9).subtract(BigDecimal.valueOf(1.5)));
        System.out.println("计算乘法: " + BigDecimal.valueOf(1.9).multiply(BigDecimal.valueOf(0.2)));
        System.out.println("计算除法: " + BigDecimal.valueOf(1.9).divide(BigDecimal.valueOf(0.2)));
    }

打印结果:

在这里插入图片描述
注意点:

  1. BigDecimal的运算结果都是返回了一个新的BigDecimal对象,并不是在原有的对象上进行操作。

代码示例:

public static void main(String[] args)  {
        BigDecimal bigDecimal = new BigDecimal("2");
        System.out.println("bigDecimal计算前的地址是:"+System.identityHashCode(bigDecimal));
        bigDecimal = bigDecimal.add(BigDecimal.valueOf(3));
        System.out.println("bigDecimal计算后的地址是:"+System.identityHashCode(bigDecimal));
    }

打印结果:
在这里插入图片描述

  1. 使用divide除法函数除不尽,出现无线循环小数的时候,就需要使用另外精确的小数位数以及舍入模式,不然会出现报错。

代码示例:

public static void main(String[] args)  {
        BigDecimal a = BigDecimal.valueOf(10), b = BigDecimal.valueOf(3);
        System.out.println(a.divide(b));
    }

报错如下:

在这里插入图片描述

解决方法可以采用四舍五入的方式,详情见下文!

(3)BigDecimal保留两位小数及舍入模式

public BigDecimal setScale(int newScale, int roundingMode)

用于格式化小数的方法,第一个值表示保留几位小数,第二个值表示格式化的类型。
8种类型如下:

格式化类型描述
ROUND_DOWN舍弃多余位数,如1.55会格式化为1.5,-1.55会格式化为-1.5
ROUND_UP进位处理,如1.52会格式化为1.6,-1.52会格式化为-1.6
ROUND_HALF_UP四舍五入,如果舍弃部分>= .5,则进位
ROUND_HALF_DOWN五舍六入,如果舍弃部分> .5,则进位
ROUND_CEILING正无穷大方向舍入模式。如果值为正数,则与ROUND_UP模式相同;如果值为负数,则与ROUND_DOWN模式相同
ROUND_FLOOR负无穷大方向舍入模式。如果值为正数,则与ROUND_DOWN模式相同;如果值为负数,则与ROUND_UP模式相同
ROUND_UNNECESSARY确认值的小数位数是否与传入第一个参数(保留小数的位数)相等,如果符合则返回值,如果不符抛出异常
ROUND_HALF_EVEN如果舍弃部门左边的数字为奇数,则与ROUND_HALF_UP模式相同,如果为偶数则与ROUND_HALF_DOWN模式相同

代码示例:

public static void main(String[] args)  {
        BigDecimal a = BigDecimal.valueOf(5.445);
        System.out.println("5.445舍弃多余位数:" + a.setScale(2, BigDecimal.ROUND_DOWN));
        System.out.println("5.445进位处理:" + a.setScale(2, BigDecimal.ROUND_UP));
        System.out.println("5.445四舍五入(舍弃部分>= .5,进位):" + a.setScale(2, BigDecimal.ROUND_HALF_UP));
        System.out.println("5.445四舍五入(舍弃部分未> .5,舍弃):" + a.setScale(2, BigDecimal.ROUND_HALF_DOWN));
        System.out.println("5.446四舍五入(舍弃部分> .5,进位):" + BigDecimal.valueOf(5.446).setScale(2, BigDecimal.ROUND_HALF_DOWN));
    }

打印结果:
在这里插入图片描述

(4)BigDecimal比较大小

public int compareTo(BigDecimal val)

BigDecimal类提供的比较值的方法,注意比较的两个值均不能为空。
a.compareTo(b)得到结果 1, 0, -1。

比较结果描述
1a 大于b
0a 等于b
-1a 小于b

代码示例:

public static void main(String[] args)  {
        BigDecimal a = BigDecimal.valueOf(1);
        BigDecimal b = BigDecimal.valueOf(2);
        BigDecimal c = BigDecimal.valueOf(1);
        BigDecimal d = BigDecimal.ZERO;
        System.out.println("1和2比较结果:" + a.compareTo(b));
        System.out.println("1和1比较结果:" + a.compareTo(c));
        System.out.println("1和0比较判断:" + (a.compareTo(d) > 0) );
    }

打印结果:
在这里插入图片描述

注意:实际业务中,不会单纯的比较谁打谁小,而是类似于第三条去判断一个布尔值。

(5)BigDecimal其他方法及常量

代码类型描述
BigDecimal.ZERO常量初始化一个为0的BigDecimal对象
BigDecimal.ONE常量初始化一个为1的BigDecimal对象
BigDecimal.TEN常量初始化一个为10的BigDecimal对象
public BigDecimal abs()方法求绝对值,不管正数还是负数,都得到正数
public BigDecimal negate()方法求相反数,正变负,负变正
public BigDecimal pow(int n)方法求乘方,如BigDecimal.valueOf(2).pow(3)的值为8
public BigDecimal max(BigDecimal val)方法两值比较,返回最大值
public BigDecimal min(BigDecimal val)方法两值比较,返回最小值
;