前言
在工作中,我们在针对金额类字段进行计算的时候,通常使用BigDecimal 类型的字段,来保留金额的精度。因此,本篇文章主要分享一下Java语言中的BigDecimal类的使用,以及使用过程中的坑。
1、概述
在日常的开发工作中,针对有数据精度要求的金额数据,都会使用BigDecimal类型(不排除有些公司使用String类型处理)。BigDecimal是Java在java.math包中提供的线程安全
的API类,可以用于表示任意精度
数字的类,它可以表示无限长度的小数
,BigDecimal 通常支持任意位数的小数部分,用来对超过16位有效位的数进行精确的运算。双精度浮点型变量double可以处理16位有效数,但在实际应用中,可能需要对更大或者更小的数进行运算和处理。一般情况下,对于那些不需要准确计算精度的数字,我们可以直接使用Float和Double处理,但是Double.valueOf(String) 和Float.valueOf(String)会丢失精度。
2、常用API方法
构造方法
- BigDecimal(String val) 创建一个具有参数所指定以字符串表示的数值的对象。
推荐使用
- BigDecimal(int val) 创建一个具有参数所指定整数值的对象
- BigDecimal(long val) 创建一个具有参数所指定长整数值的对象
- BigDecimal(double val) 创建一个具有参数所指定双精度值的对象。
不推荐使用,因为存在精度丢失问题
运算方法
- add(BigDecimal) BigDecimal对象中的值相加
- subtract(BigDecimal) BigDecimal对象中的值相减
- multiply(BigDecimal) BigDecimal对象中的值相乘
- divide(BigDecimal) BigDecimal对象中的值相除
- abs() 将BigDecimal对象中的值转换成绝对值
- doubleValue() 将BigDecimal对象中的值转换成双精度数
- floatValue() 将BigDecimal对象中的值转换成单精度数
- longValue() 将BigDecimal对象中的值转换成长整数
- intValue() 将BigDecimal对象中的值转换成整数
- compareTo(BigDecimal val) 比较大小,返回int类型。0(相等) 1(大于) -1(小于)
- max(BigDecimal val) 两值比较,返回最大值
- negate() 求相反数,正变负,负变正
- pow(int n) 求乘方,如BigDecimal.valueOf(2).pow(3)的值为8
- toPlainString() 不使用任何指数
以上就是BigDecimal的主要API方法和功能介绍
3、数据转换
3.1、BigDecimal的八种舍入模式
BigDecimal.setScale()方法用于格式化小数点
舍入模式 | 作用 |
---|---|
setScale(1,BigDecimal.ROUND_DOWN) | 直接删除多余的小数位 eg: 3.35会变成3.3 |
setScale(1,BigDecimal.ROUND_UP) | 进位处理 eg: 3.35变成4.4 |
setScale(1,BigDecimal.ROUND_HALF_UP) | 四舍五入 eg: 3.35变成3.4 |
setScaler(1,BigDecimal.ROUND_HALF_DOWN) | 四舍五入 , 3.35变成3.3,如果是5以下则向下舍 |
setScaler(1,BigDecimal.ROUND_CEILING) | 接近正无穷大的舍入 |
setScaler(1,BigDecimal.ROUND_FLOOR) | 接近负无穷大的舍入,数字>0和ROUND_UP作用一样,数字<0和ROUND_DOWN作用一样 |
setScaler(1,BigDecimal.ROUND_HALF_EVEN) | 向最接近的数字舍入,如果与两个相邻数字的距离相等,则向相邻的偶数舍入 |
3.2 BigDecimal格式化、小数点转换
public static void main(String[] s){
//默认使用的是进位方式是RoundingMode.HALF_EVEN,此舍入模式也称为“银行家算法”,主要在美国使用
DecimalFormat df = new DecimalFormat("###,##0.00");
System.out.println(df.format(new BigDecimal("12333.435")));
System.out.println(df.format(new BigDecimal(0)));
System.out.println(df.format(new BigDecimal("0.04")));
System.out.println(df.format(new BigDecimal("0.04444")));
System.out.println(df.format(new BigDecimal("0.206")));
System.out.println(df.format(new BigDecimal("1.22")));
}
3.3、货币格式化与百分比格式化
NumberFormat对象:
getCompactNumberInstance();返回FORMAT带有"SHORT"格式样式的默认语言环境 的紧凑数字格式 。
getCurrencyInstance(Locale inLocale);返回指定语言环境的货币格式。若是不指定参数,则以默认语言为参数。
getInstance(Locale inLocale);返回指定语言环境的通用数字格式。若是不指定参数,则以默认语言为参数。
getPercentInstance(Locale inLocale);返回指定语言环境的百分比格式。若是不指定参数,则以默认语言为参数。
public static void main(String[] s){
NumberFormat currency = NumberFormat.getCurrencyInstance(); //建立货币格式化引用
NumberFormat percent = NumberFormat.getPercentInstance(); //建立百分比格式化引用
percent.setMinimumFractionDigits(2);//设置数的小数部分所允许的最小位数(如果不足后面补0)
percent.setMaximumFractionDigits(3);//设置数的小数部分所允许的最大位数(如果超过会四舍五入)
BigDecimal amount = new BigDecimal("3333.42"); //金额
BigDecimal interestRate = new BigDecimal("1.0004"); //利率
BigDecimal interest = amount.multiply(interestRate); //相乘
System.out.println("金额: " + currency.format(amount));
System.out.println("利率: " + percent.format(interestRate));
System.out.println("利息: " + currency.format(interest));
}
4、BigDecimal常见问题
4.1 等值比较
等值比较重 BigDecimal 中提供了 compareTo 方法,在很多时候需要使用compareTo 比较两个值。使用equals方法,值是比较不出来的。
4.2 使用BigDecimal进行计算时参数不能为NULL
4.3 创建 BigDecimal精度丢失的坑
看下面代码
public static void main(String[] s){
BigDecimal b1= new BigDecimal(0.1113);
System.out.println(b1);
BigDecimal b2= BigDecimal.valueOf(0.13); //底层也是将值转成String 进行数据类型转换
System.out.println(b2);
BigDecimal b3= new BigDecimal("0.1111112323111111111111111111111234");
System.out.println(b3);
BigDecimal b4= BigDecimal.valueOf(0.1111112323111111111111111111111234);
System.out.println(b4);
}
运行结果
0.11129999999999999615862833479695837013423442840576171875
0.13
0.1111112323111111111111111111111234
0.11111123231111111
因此 在使用BigDecimal构造函数时,尽量传递字符串而非浮点类型;
5、总结
我们日常使用的API要知其然还要知其所以然,了解其原理和坑,在开发中,我们才能使用的得心应手,避免出现问题。以上就是本次分享的所有内容,如有不足,请多多指教。