文章目录
1.概述
前面我们学习基本类型变量的时候有做过相关案例,浮点数存在运算不精确的问题,面对需要精确运算浮点数结果的场景中,其无法胜任相关的代码开发工作,BigDecimal就是用来解决此问题的。
2.创建对象
方式一 :
BigDecimal(double val)
将double转换为BigDecimal,后者是double的二进制浮点值十进制表示形式,有坑!
方式二 :
BigDecimal(String val)
将String类型字符串的形式转换为BigDecimal
3.常用方法
Add(BigDecimal bd)
: 做加法运算Subtract(BigDecimal bd)
: 做减法运算Multiply(BigDecimal bd)
: 做乘法运算Divide(BigDecimal bd)
: 做除法运算,除不尽时会抛异常Divide(BigDecimal bd,保留位数,舍入方式)
: 除不尽时使用setScale(保留位数,舍入方式)
: 同上pow(int n)
: 求数据的几次幂compareTo(BigDecimal bd)
: 比较俩个BigDecimal值的大小,返回值为一个整数,当前对象与比较对象相等时返回“0”,小于时返回“-1”,大于时返回“1”;
4.练习:测试常用方法
package partThree;
import java.math.BigDecimal;
import java.util.Scanner;
public class TestBigDecimal {
public static void main(String[] args) {
//f1();//使用普通的 +-*/ 四则运算,暴露出浮点数运算不精确的问题
//f2();//使用BigDecimal来解决浮点数运算不精确的问题
f3();//比较俩个BigDecimal数的大小
}
private static void f3() {
while (true){
//1.提示并接收用户输入的浮点数类型的数字
System.out.println("请输入您要比较的两个小数:");
double a = new Scanner(System.in).nextDouble();
double b = new Scanner(System.in).nextDouble();
//直接将浮点数类型的数字转为字符串后创建为BigDecimal类型对象
BigDecimal b1 = new BigDecimal(a+"");
BigDecimal b2 = new BigDecimal(b+"");
//2.进行比较
System.out.println("使用”==“方法比较"+b1+"与"+b2+"的值,结果为:"+ (a == b));
System.out.println("使用”equals“方法比较"+b1+"与"+b2+"的值,结果为:"+ (b1.equals(b2)));
if((b1.compareTo(b2)) == 0){
System.out.println("使用”compareTo“方法比较"+b1+"与"+b2+"的值,结果为:相等");
}else if(b1.compareTo(b2) == 1){
System.out.println("使用”compareTo“方法比较"+b1+"与"+b2+"的值,结果为:不相等");
}else {
System.out.println("比较结果出错,请联系开发人员");
}
}
}
private static void f2() {
//1.提示并接收用户输入的两个小数
System.out.println("请输入您要计算的两个小数:");
double a = new Scanner(System.in).nextDouble();
double b = new Scanner(System.in).nextDouble();
//2.创建工具类对象,把基本类型a和b交给工具类对象BigDecimal来保存
/*1.最好不要用double作为构造函数的参数,不然还会有不精确的现象,有坑!!!*/
/*2.最好使用重载的,参数类型是String的构造函数
* double转String,直接拼个空串就可以*/
BigDecimal bd1 = new BigDecimal(a+"");//double+" " 将其转为了String
BigDecimal bd2 = new BigDecimal(b+"");//double+" " 将其转为了String
//3.通过BigDecimal的对象来调用其方法,实现精确运算
//3.1 定义BigDecimal类型的引用类型变量来保存结果
BigDecimal bd3;
//3.2 Add(BigDecimal bd) : 做加法运算
bd3 = bd1.add(bd2);
System.out.println("加法的结果是:"+bd3);
//3.3 Subtract(BigDecimal bd) : 做减法运算
bd3 = bd1.subtract(bd2);
System.out.println("减法的结果是:"+bd3);
//3.4 Multiply(BigDecimal bd) : 做乘法运算
bd3 = bd1.multiply(bd2);
System.out.println("乘法的结果是:"+bd3);
//3.5 Divide(BigDecimal bd) : 做除法运算,除不尽时会抛异常
/*3.除法运算,除不尽时会抛出异常ArithmeticException*/
//方案一:(除不尽时有问题)
//bd3 = bd1.divide(bd2);
/*divide(m,n,o)
m是要除以哪个对象,n指要保留几位,o指舍入方式(比如四舍五入)*/
//方案二:
bd3 = bd1.divide(bd2,3,BigDecimal.ROUND_HALF_UP);
System.out.println("除法的结果是:"+bd3);
}
private static void f1() {
//1.提示并接收用户输入的两个小数
System.out.println("请输入您要计算的两个小数:");
double a = new Scanner(System.in).nextDouble();
double b = new Scanner(System.in).nextDouble();
//2.做运算
System.out.println(a + b);//不精确
System.out.println(a - b);//不精确
System.out.println(a * b);//不精确
System.out.println(a / b);//不精确
}
}
5.关于BigDecimal中的集中四舍五入方法
ROUND_HALF_UP
四舍五入,五入 如:4.4结果是4; 4.5结果是5ROUND_HALF_DOWN
五舍六入,五不入 如:4.5结果是4; 4.6结果是5ROUND_HALF_EVEN
公平舍入(银行常用)
比如:在5和6之间,靠近5就舍弃成5,靠近6就进位成6,如果是5.5,就找偶数,变成6ROUND_UP
直接进位,不算0.1还是0.9,都进位ROUND_DOWN
直接舍弃,不算0.1还是0.9,都舍弃ROUND_CEILING(天花板)
向上取整,取实际值的大值
朝正无穷方向round 如果为正数,行为和round_up一样,如果为负数,行为和round_down一样ROUND_FLOOR(地板)
向下取整,取实际值的小值
朝负无穷方向round 如果为正数,行为和round_down一样,如果为负数,行为和round_up一样
6.BigDecimal格式化
BigDecimal的常用方法中,只提供了除法运算的简单保留小数格式化操作;
其可以与DecimalFormat
结合使用,从而对金额格式化,如小数点后面统一保留两位,不够两位的补零,多余两位的舍入都进行操作;
package partThree;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
public class BigDecimalFormat {
public static void main(String[] args) {
//公平舍入操作
System.out.println(formatToNumber(new BigDecimal("12333.435")));
//货币格式化操作
System.out.println("货币格式化操作"+ formatCurrencyInstance(new BigDecimal("1931567.895")));
//百分比格式化操作
System.out.println("百分比格式化操作"+formatPercentInstance(17153));
System.out.println("百分比格式化操作"+formatPercentInstance(17153.951287));
}
public static String formatToNumber(BigDecimal obj){
// DecimalFormat默认使用的是进位方式是RoundingMode.HALF_EVEN,此舍入模式也称为“银行算法”,主要在银行业务中使用。
DecimalFormat df = new DecimalFormat("###,##0.00");
/*
* new DecimalFormat(“###,##0.00”)小数点前面需要有个0
* 这样0-1之间的数字才会正常格式化;若##0.00的小数点前面没有0
* 则0-1之间的数字会被丢失掉小数点前的0
* */
//DecimalFormat df = new DecimalFormat("###,##.00");
return df.format(obj);
}
public static String formatCurrencyInstance(BigDecimal obj){
NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立货币格式化引用
return currency.format(obj);
}
public static String formatPercentInstance(Object obj){
NumberFormat percent = NumberFormat.getPercentInstance(); //建立百分比格式化引用
percent.setMinimumFractionDigits(2);//设置数的小数部分所允许的最小位数(如果不足后面补0)
percent.setMaximumFractionDigits(3);//设置数的小数部分所允许的最大位数(如果超过会四舍五入)
return percent.format(obj);
}
}
7. BigDecimal三种字符串输出的坑
在BigDecimal
转换成字符串时,有可能输出非你预期的结果。例如:
可以看到三种方式输出的结果可能都不相同,可能这个并不是预期的结果 ,BigDecimal 有三个方法可以转为相应的字符串类型,切记不要用错:
以下内容介绍java.math.BigDecimal
下的三个toString方法的区别及用法:
toPlainString()
: 不使用任何指数。toString()
:有必要时使用科学计数法。toEngineeringString()
:有必要时使用工程计数法。 工程记数法是一种工程计算中经常使用的记录数字的方法,与科学技术法类似,但要求10的幂必须是3的倍数
8.BigDecimal使用中的注意事项
- 我们一般使用BigDecimal是为了解决浮点数计算时的精度丢失问题,其共有俩种创建方式,一种是将浮点数转为BigDecimal对象,另一种是将字符串转为BigDecimal对象,而浮点数转为BigDecimal对象时BigDecimal是double的二进制浮点值十进制表示,在转换过程中还会存在浮点数计算时的精度丢失问题,所以我们一般只通过字符串类型的对象进行创建BigDecimal数,当遇到浮点数类型的对象时,总要将其转为字符串类型后,再进行BigDecimal对象的创建;
- 一般在比较两个引用类型的值是否相等时,都是用
equals
方法,但是,在BigDecimal 中使用equals
不仅会比较值是否相等,还会比较精度是否相等,可能会导致结果错误,例如 1.2 和 1.200 这俩个数,其精度是不相同的,但是其值对我们来说是相等的;BigDecimal 中提供了compareTo
方法,在很多时候需要使用compareTo
比较两个值而不是equals
方法;但如果同时想比较俩个数的精度是否相等,那么可以使用equals
方法; - BigDecimal 并不代表无限精度,当在两个数除不尽的时候,就会出现无限精度的坑;也就是说,比如我们做除法运算时,如果商是一个无限小数,将会抛出
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.
异常,此时需要我们指定结果的具体精度,在日常开发使用BigDecimal 时,一定要考虑此问题; - BigDecimal三种字符串输出的坑;
- 使用BigDecimal进行计算时参数不能为NULL;在使用BigDecimal类型进行计算时,进行加、减、乘、除、比较大小时,一定要保证参与计算的两个值不能为空(null),否则会抛出java.lang.NullPointerException异常。
- 使用BigDecimal进行除法计算时被除数不能为0,其实不只是BigDecimal,在整数型除法运算中也是这样;并且在计算中,不要想着使用什么乘法交换律,老老实实按照程序运行顺序去执行;