在java.math包中提供了对大数字的操作类,用于进行高精确计算,如BigInteger,BigDecimal类 。而平常我们开发中使用最多的float和double只能适用于一般的科学和工程计算,如果要在比较精确的计算方面如货币,那么使用float和double会相应的丢失精度,因此用于精密计算大数字的类BigDecimal就必不可少了 。所以BigDecimal适合商业计算场景,用来对超过16位有效位的数进行精确的运算 。但是BigDecimal的使用并不像float和double那样,使用不当造成的后果更严重,下面就来看下我们项目中踩过BigDecimal的坑:
Bigdecimal的坑
一. BigDecimal的初始化精度丢失问题 先来看下面代码的运行结果:
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);
输出结果:
bd1=0.1000000000000000055511151231257827021181583404541015625bd2=0.1bd3=0.1
如果是float或double类型转Bigdecimal,不要使用new BigDecimal()转, 使用valueOf()方法 或 new BigDecimal(“”)转成string,否则有可能出现精度问题 。
《Effective Java》这本书里说过:
如果需要精确的答案,请避免使用float和double因为float和double执行的是二进制浮点运算,二进制有些情况下不能准确的表示一个小数,就像十进制不能准确的表示1/3(1/3=0.3333…)也就是说二进制表示小数的时候只能够表示能够用1/(2^n)的和的任意组合,例如:
0.5能够表示,因为它可以表示成为1/20.75也能够表示,因为它可以表示成为1/2 1/(2^2)0.875也能够表示,因为它可以表示成为1/2 1/(2^2) 1/(2^3)但是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)
直接抛出运算异常,这种错误比较隐蔽,因为你本地测试可能只测试了能整除的情况,但忽视了除不尽有余数的情况,那这段代码发布生产环境的话就是一个bug!
推荐阅读
- 怎么去除衣服上的铁锈
- 自行车车把生锈了怎么办
- 361÷9列竖式怎么列 除法列竖式口诀
- 利用计算机读取整茶色泽参数的方法研究
- 荣耀手机声音怎么放大 怎么声音那么小教你4种排除法!
- 撕强力胶的方法 如何撕强力胶
- 二年级下学期数学除法怎么讲解 可以这样讲解
- 数学常识中什么是除法 除法的含义
- 去掉鱼骨方法 这两种方法最常用
- 紧致法龄纹的方法 消除法令纹的方法