在java.math
包中提供了对大数字的操做类,用于进行高精确计算,如BigInteger
,BigDecimal
类。而日常咱们开发中使用最多的float和double只能适用于通常的科学和工程计算,若是要在比较精确的计算方面如货币,那么使用float和double会相应的丢失精度,所以用于精密计算大数字的类BigDecimal
就必不可少了。因此BigDecimal
适合商业计算场景,用来对超过16位有效位的数进行精确的运算。可是BigDecimal的使用并不像float和double那样,使用不当形成的后果更严重,下面就来看下咱们项目中踩过BigDecimal
的坑: html
一. BigDecimal的初始化精度丢失问题
先来看下面代码的运行结果:java
BigDecimal bd1 = new BigDecimal(0.1); System.out.println("bd1="+bd1); BigDecimal bd2 = new BigDecimal("0.1"); System.out.println("bd2="+bd2); BigDecimal bd3 = BigDecimal.valueOf(0.1); System.out.println("bd3="+bd3);
输出结果:ide
bd1=0.1000000000000000055511151231257827021181583404541015625 bd2=0.1 bd3=0.1
若是是float或double类型转Bigdecimal
,不要使用new BigDecimal()
转, 使用valueOf()
方法 或new BigDecimal("")
转成string,不然有可能出现精度问题。spa
《Effective Java》这本书里说过:code
若是须要精确的答案,请避免使用float和doublehtm
由于float和double执行的是二进制浮点运算,二进制有些状况下不能准确的表示一个小数,就像十进制不能准确的表示1/3(1/3=0.3333...)
也就是说二进制表示小数的时候只可以表示可以用1/(2^n)的和的任意组合,例如:blog
-
0.5可以表示,由于它能够表示成为1/2ci
-
0.75也可以表示,由于它能够表示成为1/2+1/(2^2)开发
-
0.875也可以表示,由于它能够表示成为1/2+1/(2^2)+1/(2^3)get
可是0.1不可以精确表示,由于它不可以表示成为1/(2^n)的和的形式
System.out.println(0.5*3); System.out.println(0.1*3);
你们能够本地执行下这两行代码,看下输出结果就知道为何二进制不能表示0.1却能够表示0.5了。因此其实不是BigDecimal
的问题,BigDecimal
就是为了知足精确运算存在的,问题出在0.1它自己就一个不许确的值,这其实跟BigDecimal
无关,但在使用的时候须要注意用法。
二. BigDecimal在进行除法运算时需设置精度,不然对于除不尽的状况会抛出异常
继续看下面的代码执行结果:
BigDecimal bd4 = new BigDecimal("10"); BigDecimal bd5 = new BigDecimal("3"); System.out.println(bd4.divide(bd5));
输出结果:
Exception in thread "main" java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result. at java.math.BigDecimal.divide(BigDecimal.java:1690) at BigDecimalTest.main(BigDecimalTest.java:38)
应该向下面这样设置小数点后的位数,以及超出后是四舍五入和向上/向下取整或者直接舍弃:
System.out.println(bd4.divide(bd5,2,BigDecimal.ROUND_DOWN));
第二个参数表示小数位数,第三个参数表示超出的位数直接舍弃(固然也能够设置四舍五入,向上取整等)
三. 不要使用BigDecimal的equals方法比较大小, 不然可能会由于精度问题致使比较结果和预期的不一致
BigDecimal bd1 = new BigDecimal("0"); BigDecimal bd2 = new BigDecimal("0.0"); System.out.println(bd1.equals(bd2)); System.out.println(bd1.[compareTo](http://javakk.com/tag/compareto "查看更多关于 compareTo 的文章")(bd2) == 0)
输出结果:
equals:false compareTo:true
若是你没法肯定你的BigDecimal
值有小数状况,最好用compareTo
!