BigDecimal类
## 1 引入
首先我们来分析一下如下程序的执行结果:
public class BigDecimalDemo01 {
public static void main(String[] args) {
System.out.println(0.09 + 0.01);
}
}
这段代码比较简单,就是计算0.09和0.01之和,并且将其结果在控制台进行输出。那么按照我们的想法在控制台输出的结果应该为0.1。那么实际的运行结果是什么呢?我们来运行一下程序,控制台的输出
结果如下所示:
0.09999999999999999
这样的结果其实就是一个丢失精度的结果。为什么会产生精度丢失呢?
在使用float或者double类型的数据在进行数学运算的时候,很有可能会产生精度丢失问题。我们都知道计算机底层在进行运算的时候,使用的都是二进制数据; 当我们在程序中写了一个十进制数据 ,在进行运算的时候,计算机会将这个十进制数据转换成二进制数据,然后再进行运算,计算完毕以后计算机会把运算的结果再转换成十进制数据给我们展示; 如果我们使用的是整数类型的数据进行计算,那么在把十进制数据转换成二进制数据的时候不会存在精度问题; 如果我们的数据是一个浮点类型的数据,有的时候计算机并不会将这个数据完全转换成一个二进制数据,而是将这个将其转换成一个无限的趋近于这个十进数的二进制数据; 这样使用一个不太准确的数据进行运算的时候, 最终就会造成精度丢失;为了提高精度,Java就给我们提供了BigDecimal供我们进行数据运算。
2 概述
查看API文档,我们可以看到API文档中关于BigDecimal类的定义如下:
BigDecimal所在包是在java.math包下,因此在使用的时候就需要进行导包。我们可以使用BigDecimal类进行更加精准的数据计算。
3 常见方法
构造方法
要用BigDecimal类,那么就需要首先学习一下如何去创建BigDecimal的对象。通过查看API文档,我们可以发现Jdk中针对BigDecimal类提供了很多的构造方法,但是最常用的构造方法是:
构造方法获取BigDecimal对象
public BigDecimal(double val)
public BigDecimal(string val)
静态方法获取BigDecimal对象
public static BigDecimal valuef(double val)
详解
1.通过传递double类型的小数来创建对象
细节:
这种方式有可能是不精确的,所以不建议使用
BigDecimal bd1 = new BigDecimal(0.01);
BigDecimal bd2 = new BigDecimal(0.09);
System.out.println(bd1);
System.out.println(bd2);
2.通过传递字符串表示的小数来创建对象
BigDecimal bd3 = new BigDecimal("0.01");
BigDecimal bd4 = new BigDecimal("0.09");
BigDecimal bd5 = bd3.add(bd4);
System.out.println(bd3);
System.out.println(bd4);
System.out.println(bd5);
3.通过静态方法获取对象
细节:
3.1.如果要表示的数字不大,没有超出double的取值范围,建议使用静态方法
3.2.如果要表示的数字比较大,超出了double的取值范围,建议使用构造方法
3.3.如果我们传递的是0~10之间的整数,包含0,包含10,那么方法会返回已经创建好的对象,不会重新new
BigDecimal bd6 = BigDecimal.valueOf(10.0);
BigDecimal bd7 = BigDecimal.valueOf(10.0);
System.out.println(bd6 == bd7);
关于3.3,注意:传递的值为整数,若是10.0等double类型的数,会重新new
了解完常见的构造方法以后,我们接下来就重点介绍一下常见的成员方法。
常见成员方法
BigDecimal类中使用最多的还是提供的进行四则运算的方法,如下:
public BigDecimal add(BigDecimal value) // 加法运算
public BigDecimal subtract(BigDecimal value) // 减法运算
public BigDecimal multiply(BigDecimal value) // 乘法运算
public BigDecimal divide(BigDecimal value) // 触发运算
接下来我们就来通过一些案例演示一下这些成员方法的使用。
案例1:演示基本的四则运算
代码如下所示:
public class BigDecimalDemo01 {
public static void main(String[] args) {
// 创建两个BigDecimal对象
BigDecimal b1 = new BigDecimal("0.3") ;
BigDecimal b2 = new BigDecimal("4") ;
// 调用方法进行b1和b2的四则运算,并将其运算结果在控制台进行输出
System.out.println(b1.add(b2)); // 进行加法运算
System.out.println(b1.subtract(b2)); // 进行减法运算
System.out.println(b1.multiply(b2)); // 进行乘法运算
System.out.println(b1.divide(b2)); // 进行除法运算
}
}
运行程序进行测试,控制台输出结果如下:
4.3
-3.7
1.2
0.075
此时我们可以看到使用BigDecimal类来完成浮点数的计算不会存在损失精度的问题。
案例2:演示除法的特殊情况
如果使用BigDecimal类型的数据进行除法运算的时候,得到的结果是一个无限循环小数,那么就会报错:ArithmeticException。 如下代码所示:
public class BigDecimalDemo02 {
public static void main(String[] args) {
// 创建两个BigDecimal对象
BigDecimal b1 = new BigDecimal("1") ;
BigDecimal b2 = new BigDecimal("3") ;
// 调用方法进行b1和b2的除法运算,并且将计算结果在控制台进行输出
System.out.println(b1.divide(b2));
}
}
运行程序进行测试,控制台输出结果如下所示:
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
at java.base/java.math.BigDecimal.divide(BigDecimal.java:1716)
at com.itheima.api.bigdecimal.demo02.BigDecimalDemo02.main(BigDecimalDemo02.java:14)
针对这个问题怎么解决,此时我们就需要使用到BigDecimal类中另外一个divide方法,如下所示:
BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)
上述divide方法参数说明:
divisor: 除数对应的BigDecimal对象;
scale: 精确的位数;
roundingMode: 取舍模式;
取舍模式被封装到了RoundingMode这个枚举类中(关于枚举我们后期再做重点讲解),在这个枚举类中定义了很多种取舍方式。最常见的取舍方式有如下几个:
UP(直接进1) , FLOOR(直接删除) , HALF_UP(4舍五入),我们可以通过如下格式直接访问这些取舍模式:枚举类名.变量名
接下来我们就来演示一下这些取舍模式,代码如下所示:
public class BigDecimalDemo02 {
public static void main(String[] args) {
// 调用方法
method_03() ;
}
// 演示取舍模式HALF_UP
public static void method_03() {
// 创建两个BigDecimal对象
BigDecimal b1 = new BigDecimal("0.3") ;
BigDecimal b2 = new BigDecimal("4") ;
// 调用方法进行b1和b2的除法运算,并且将计算结果在控制台进行输出
System.out.println(b1.divide(b2 , 2 , RoundingMode.HALF_UP));
}
// 演示取舍模式FLOOR
public static void method_02() {
// 创建两个BigDecimal对象
BigDecimal b1 = new BigDecimal("1") ;
BigDecimal b2 = new BigDecimal("3") ;
// 调用方法进行b1和b2的除法运算,并且将计算结果在控制台进行输出
System.out.println(b1.divide(b2 , 2 , RoundingMode.FLOOR));
}
// 演示取舍模式UP
public static void method_01() {
// 创建两个BigDecimal对象
BigDecimal b1 = new BigDecimal("1") ;
BigDecimal b2 = new BigDecimal("3") ;
// 调用方法进行b1和b2的除法运算,并且将计算结果在控制台进行输出
System.out.println(b1.divide(b2 , 2 , RoundingMode.UP));
}
}
小结:后期在进行两个数的除法运算的时候,我们常常使用的是可以设置取舍模式的 “HALF_UP” 方法。
舍入模式图解:
4 底层存储方式:
把数据看成字符串,遍历得到里面的每一个字符,把这些字符在ASCII码表上的值,都存储到数组中。
负数有符号位,正数没有符号位