前言:
互联网公司,对于BigDecimal的使用,还是较为频繁的,那么就会涉及到关于这个类型的种种问题.
1:为什么使用BigDecimal
首先java八大基本类型真的很基本,4个整型搞不了小数,double和float搞的了小数,但搞不好,关键时刻就调链子,当然这也和他们存储方式有关(二进制无法精确的表示1/10),天生注定搞不了精密计算,尤其是和钱相关的.
所以一般我们对于精密点的计算和涉及货币的计算,都会使用BigDecimal.他的出现,就是为了解决基本数据类型浮点数不能进行精确计算的问题.
2:如何使用BigDecimal
就说最常用的:
2.1.初始化,一般使用这个构造方法:new BigDecimal(String value);
其他的不再多说,某度很多,只能说我们一般都会用这个String类型的入参
关于BigDecimal的初始化,有一个坑要注意,就是尽量使用String类型的去初始化。
不信的话,可以看看下面的例子:BigDecimal a = new BigDecimal(1.01);BigDecimal b = new BigDecimal(1.02);BigDecimal c = new BigDecimal("1.01");BigDecimal d = new BigDecimal("1.02");System.out.println(a.add(b));System.out.println(c.add(d));
最终的打印结果是:
2.0300000000000000266453525910037569701671600341796875
2.03
很明显,也是精度问题。这块要注意。
2.2.基本运算
add(BigDecimal) BigDecimal对象中的值相加,然后返回这个对象。
subtract(BigDecimal) BigDecimal对象中的值相减,然后返回这个对象。
multiply(BigDecimal) BigDecimal对象中的值相乘,然后返回这个对象。
divide(BigDecimal) BigDecimal对象中的值相除,然后返回这个对象。
toString() 将BigDecimal对象中的值以字符串形式返回。
doubleValue() 将BigDecimal对象中的值以双精度数返回。
floatValue() 将BigDecimal对象中的值以单精度数返回。
longValue() 将BigDecimal对象中的值以长整数返回。
intValue() 将BigDecimal对象中的值以整数返回。
3.采坑经验(敲黑板的重点!!!)
3.1.将BigDecimal类性的值(10.1000)传给页面后,页面展示的(10.1000)不是我们想要的(10.1),该怎么处理?
BigDecimal money;public BigDecimal getMoney() {return money;}最开始上面的这种处理方式,如果money是10.1000,那么get方法也会拿到10.1000,但其实我们想要的是10.1就好了.
------------------------------------------------------------------------- 那怎么办,直接上最终版(重写个getMoneyString方法):public String getMoneyString() {return money.stripTrailingZeros().toPlainString();}这个方法返回的,就是10.1,String类型的
--------------------------------------------------------------------------解释下:1.stripTrailingZeros()方法官方文档对返回的解释是:a numerically equal BigDecimal with any trailing zeros removed.而且从方法名就可以看出来,就是去掉末尾的0.那么,为什么还要用toPlainString()方法呢?看个例子:new BigDecimal("60000.000").stripTrailingZeros();这个结果是6E+4,而不是我们想要的60000,而且官方文档上也说明了,返回的值,会用科学计数法来表示,所以看下个方法2.toPlainString()官方文档对返回的解释是:a string representation of this BigDecimal without an exponent field.就是返回一个不用指数表示的字符串形式的值.所以两个方法连用,就可以达到对BigDecimal的初步控制.
3.2.值的比较用compareTo,不要用equals
compareTo:左边比右边数大,返回1,相等返回0,比右边小返回-1
equals:BigDecimal重写了equals,比较的是数值
3.3.除法要养成习惯去设置精度,以免出现无限循环的小数而抛异常
(java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result)
new BigDecimal("10").divide(new BigDecimal("3"));
这个就会抛出上面的异常.所以要用下面的new BigDecimal("10").divide(new BigDecimal("3"), 4, BigDecimal.ROUND_HALF_DOWN);
这个代表10除以3,结果保留4位小数,采用四舍五入保留
所以结果就是:3.3333
3.4.既然用了BigDecimal了,就应避免再与浮点类型互相转换,否则精度会受很大的影响
4.BigDecimal格式化
可以使用NumberFormat类的format(BigDecimal)方法,利用NumberFormat不同类型的实例,对BigDecimal转换不同的格式.详细可以查查NumberFormat的各种实例方法,比如:
NumberFormat.getCurrencyInstance();
NumberFormat.getCurrencyInstance();
NumberFormat.getInstance();
…
根据不同的需求,使用不同的格式…
下面提供两个现成的转换精度的方法
/**** @param obj 传入的BigDecimal类型* @return 返回String类型,保留两位小数(四舍五入),不够补0*/public static String formatToStringWithTwoPlaces(BigDecimal obj) {DecimalFormat df = new DecimalFormat("#.00");if (obj.compareTo(BigDecimal.ZERO) == 0) {return "0.00";} else if (obj.compareTo(BigDecimal.ZERO) > 0 && obj.compareTo(new BigDecimal(1)) < 0) {return "0" + df.format(obj).toString();} else {return df.format(obj).toString();}}//这个方法效果和上一个一样,两种不同的实现罢了public static String getDecimalStr(BigDecimal bd) {String bdStr;if (bd != null) {bdStr = bd.setScale(2, RoundingMode.HALF_EVEN).toPlainString();} else {bdStr = "0.00";}return bdStr;}public static void main(String[] args) {//各种类型测试BigDecimal bigDecimal1 = new BigDecimal("0");System.out.println(formatToStringWithTwoPlaces(bigDecimal1));//0.00BigDecimal bigDecimal11 = new BigDecimal("0.6");System.out.println(formatToStringWithTwoPlaces(bigDecimal11));//0.60BigDecimal bigDecimal12 = new BigDecimal("0.6666");System.out.println(formatToStringWithTwoPlaces(bigDecimal12));//0.67BigDecimal bigDecimal2 = new BigDecimal("2");System.out.println(formatToStringWithTwoPlaces(bigDecimal2));//2.00BigDecimal bigDecimal3 = new BigDecimal("2.1");System.out.println(formatToStringWithTwoPlaces(bigDecimal3));//2.10BigDecimal bigDecimal4 = new BigDecimal("2.22");System.out.println(formatToStringWithTwoPlaces(bigDecimal4));//2.22BigDecimal bigDecimal5 = new BigDecimal("2.333");System.out.println(formatToStringWithTwoPlaces(bigDecimal5));//2.33BigDecimal bigDecimal15 = new BigDecimal("0");System.out.println(getDecimalStr(bigDecimal15));//0.00BigDecimal bigDecimal115 = new BigDecimal("0.6");System.out.println(getDecimalStr(bigDecimal115));//0.60BigDecimal bigDecimal125 = new BigDecimal("0.6666");System.out.println(ConvertUtil.getDecimalStr(bigDecimal125));//0.67BigDecimal bigDecimal25 = new BigDecimal("2");System.out.println(getDecimalStr(bigDecimal25));//2.00BigDecimal bigDecimal35 = new BigDecimal("2.1");System.out.println(getDecimalStr(bigDecimal35));//2.10BigDecimal bigDecimal45 = new BigDecimal("2.22");System.out.println(getDecimalStr(bigDecimal45));//2.22BigDecimal bigDecimal55 = new BigDecimal("2.333");System.out.println(getDecimalStr(bigDecimal55));//2.33}
5.BigDecimal取整
public static void main(String[] args) {BigDecimal bigDecimal = new BigDecimal(6.66);//6.66除以6,向上取整,结果为2BigDecimal divide = bigDecimal.divide(BigDecimal.valueOf(6),0, BigDecimal.ROUND_UP);System.out.println(divide);//2//6.66除以6,不报错,但有这么多小数跟在后面BigDecimal divide2 = bigDecimal.divide(BigDecimal.valueOf(6), BigDecimal.ROUND_UP);System.out.println(divide2);//1.110000000000000023684757858670006195704142252605//1.110000000000000023684757858670006195704142252605向上取整,结果为2BigDecimal bigDecimal1 = divide2.setScale(0, BigDecimal.ROUND_UP);System.out.println(bigDecimal1);//6.66除以6,报错:java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result.BigDecimal divide1 = bigDecimal.divide(BigDecimal.valueOf(6));System.out.println(divide1);//报错}
各个roundingMode详解如下:
ROUND_UP:非0时,舍弃小数后(整数部分)加1,比如1.11结果为2,-1.11结果为 -2
ROUND_DOWN:直接舍弃小数
ROUND_CEILING:如果 BigDecimal 是正的,则做 ROUND_UP 操作;如果为负,则做 ROUND_DOWN 操作 (一句话:取附近较大的整数)
ROUND_FLOOR: 如果 BigDecimal 是正的,则做 ROUND_DOWN 操作;如果为负,则做 ROUND_UP 操作(一句话:取附近较小的整数)
ROUND_HALF_UP:四舍五入(取更近的整数)
ROUND_HALF_DOWN:跟ROUND_HALF_UP 差别仅在于0.5时会向下取整
ROUND_HALF_EVEN:取最近的偶数
ROUND_UNNECESSARY:不需要取整,如果存在小数位,就抛ArithmeticException 异常