BigDecimal是各大厂商Java面试中经常被问到的一个知识点。《阿里巴巴 Java 开发手册》提到:“为了避免精度损失,可以使用BigDecimal进行浮点运算。”浮点数的运算还有失精度的风险吗?确实会!示例代码:floata=2.0f-1.9f;浮动b=1.8f-1.7f;System.out.println(a);//0.100000024System.out.println(b);//0.099999905System.out.println(a==b);//false为什么浮动时会有精度损失的风险-点数是float还是double运算?这与计算机存储浮点数的机制有很大关系。我们知道计算机是二进制的,而计算机在表示数字的时候,宽度是有限的,小数无限循环存入计算机只能截断,所以小数的精度会丢失。这也解释了为什么浮点数不能用二进制准确表示。例如,十进制的0.2不能准确地转换成二进制小数://0.2转换成二进制数的过程就是乘以2,直到没有小数为止。//在这个计算过程中,得到的整数部分从上到下排列就是二进制结果。0.2*2=0.4->00.4*2=0.8->00.8*2=1.6->10.6*2=1.2->10.2*2=0.4->0(发生循环)......更多关于浮点数,它推荐看一下计算机系统基础(四)浮点数一文。BigDecimal简介BigDecimal可以实现对浮点数的运算而不损失精度。通常,大部分需要浮点数精确计算结果的业务场景(比如涉及到钱的场景)都是通过BigDecimal来完成的。《阿里巴巴 Java 开发手册》提到:对于浮点数之间的等价判断,基本数据类型不能用==比较,封装数据类型不能用equals判断。具体原因上面已经详细介绍过,这里不再赘述。解决浮点运算丢失精度的问题,可以直接使用BigDecimal定义浮点数的值,然后进行浮点运算。BigDecimala=newBigDecimal("1.0");BigDecimalb=newBigDecimal("0.9");BigDecimalc=newBigDecimal("0.8");BigDecimalx=a.subtract(b);BigDecimaly=b.subtract(c);System.out.println(x.compareTo(y));//0BigDecimal常用方法创建推荐使用其BigDecimal(Stringval)构造方法或BigDecimal.valueOf(doubleval)static创建对象的方法。《阿里巴巴 Java 开发手册》也提到了这部分,如下图。加减乘除add方法用于两个BigDecimal对象相加,subtract方法用于两个BigDecimal对象相减。multiply方法用于将两个BigDecimal对象相乘,divide方法用于将两个BigDecimal对象相除。BigDecimala=newBigDecimal("1.0");BigDecimalb=newBigDecimal("0.9");System.out.println(a.add(b));//1.9System.out.println(a.subtract(b));//0.1System.out.println(a.multiply(b));//0.90System.out.println(a.divide(b));//无法除法,抛出ArithmeticException异常System.out.println(a.divide(b,2,RoundingMode.HALF_UP));//1.11需要什么这里需要注意的是,我们在使用divide方法的时候,尽量使用3参数的版本,RoundingMode不要选择UNNECESSARY,否则很可能会遇到ArithmeticException(无法划分无限重复的小数),其中scale表示如何保留多少小数位,roundingMode表示保留规则。publicBigDecimaldivide(BigDecimaldivisor,intscale,RoundingModeroundingMode){returndivide(divisor,scale,roundingMode.oldMode);}有很多保留规则,这里有几个:publicenumRoundingMode{//2.5->3,1.6->2//-1.6->-2,-2.5->-3UP(BigDecimal.ROUND_UP),//2.5->2,1.6->1//-1.6->-1,-2.5->-2DOWN(BigDecimal.ROUND_DOWN),//2.5->3,1.6->2//-1.6->-1,-2.5->-2CEILING(BigDecimal.ROUND_CEILING),//2.5->2,1.6->1//-1.6->-2,-2.5->-3FLOOR(BigDecimal.ROUND_FLOOR),//2.5->3,1.6->2//-1.6->-2,-2.5->-3HALF_UP(BigDecimal.ROUND_HALF_UP),//...}大小比较a.compareTo(b):返回-1表示a小于b,0表示a等于b,1表示a大于b。BigDecimala=newBigDecimal("1.0");BigDecimalb=newBigDecimal("0.9");System.out.println(a.compareTo(b));//1通过setScale方法Decimals保留小数位和保留规则。保留规则有很多种,你不用记住,IDEA会提示你。BigDecimalm=newBigDecimal("1.255433");BigDecimaln=m.setScale(3,RoundingMode.HALF_DOWN);System.out.println(n);//《阿里巴巴 Java 开发手册》中提到的1.255BigDecimal等价比较问题:BigDecimal代码示例使用equals()方法进行相等比较时出现问题:BigDecimala=newBigDecimal("1");BigDecimalb=newBigDecimal("1.0");System.out.println(a.equals(b));//false这是因为equals()方法不仅要比较值(value),还要比较精度(scale),而compareTo()方法在比较时忽略了精度。1.0的比例为1,1的比例为0,因此a.equals(b)的结果为false。compareTo()方法可以比较两个BigDecimal值,如果它们相等则返回0,如果第一个数字大于第二个数字则返回1,否则返回-1。BigDecimala=newBigDecimal("1");BigDecimalb=newBigDecimal("1.0");System.out.println(a.compareTo(b));//0总结浮点数不能用二进制精确表示,所以存在精度损失的风险。但是,Java提供了BigDecimal来处理浮点数。BigDecimal的实现利用了BigInteger(用于操作大整数),不同的是BigDecimal增加了小数位数的概念。
