在电商与金融场景中,对数据的敏感程度较高,一旦出现了精度的问题,如果涉及到金钱,精度问题造成了误差,误差就会演变成错误,会造成一定的经济损失,这是大家都不希望见到的
所以今天,来学习一下,如何正确的使用double、Double与BigDecimal
double与Doubl e
double是一种基本数据类型,创建引用,存放在栈中,8字节
,不可为NULL
Double是一个类,创建对象,存放在堆中,可以为NULL
Double是对double类型的封装(也可以说是包装类),内置很多方法可以实现String到double的转换,以及获取各种double类型的属性值(MAX_VALUE,SIZE等)
double可以自动拆箱和自动装箱,把double类型数据转换成Double,那么这个数据就可以使用Double中的所有方法
另外,由于Double是包装类型(可为NULL),在接口中使用的时候,也只能使用包装类型,不能使用基本数据类型,例如:ThreadLocal< Double >、List< Double >、Map< Double ,Object>等
double在使用中,还会有一些隐藏的坑
1.计算时出现不准确的问题
System.out.println(12.3 + 45.6); // 57.900000000000006 System.out.println(1.14 / 100); // 0.011399999999999999
double的小数部分容易出现使用二进制无法准确表示
如十进制的0.1,0.2,0.3,0.4 都不能准确表示成二进制
可参考: https://blog.csdn.net/Lixuanshengchao/article/details/82049191
2.判断double是否相等需要注意
在使用 == 判断两个double是否相等时,如果double超过了16位,会出现问题
16位,不相等
double a = 1.1234567890123451; double b = 1.1234567890123452; System.out.println(a == b); // false
17位,相等
double a = 1.12345678901234561; double b = 1.12345678901234567; System.out.println(a == b); // true
如果在使用场景中,对数据的敏感程度不高,可以容忍精度损失的情况,例如:计算占比,百分比等情况,本身对数据的结果是未知的,且数据不敏感, 推荐使用double,节省了时间和空间
Double与BigDecimal
与Double相同,BigDecimal也是一个类, 创建对象,存放在堆中
上文我们说过,double超过了16位就会有一些精度损失的问题,那么BigDecimal就是用来对超过16位有效位的数进行精确的运算
因为BigDecimal是一个类,所以在运算中无法直接进行 + – x ÷ 等算术运算符的的操作,而要调用对应的方法
同样在BigDecimal在使用中也要注意一些问题
1.new BigDecimal(double) 与 BigDecimal.valueOf(double)
先来举个例子
BigDecimal bd1 = BigDecimal.valueOf(12.3); // 12.3 BigDecimal bd2 = new BigDecimal(12.3); // 12.300000000000000710542735760100185871124267578125
通过上面的例子可以看出,new BigDecimal的方式,可能不是你想要的结果,这种问题,早在 在源码中 BigDecimal(double) 构造函数的注释中就有说明(感兴趣的请查看源码),说明如下:
1)参数类型为double的构造方法的结果有一定的不可预知性
有人可能认为在Java中写入newBigDecimal(0.1)所创建的BigDecimal正好等于 0.1(非标度值 1,其标度为 1),但是它实际上等于
0.1000000000000000055511151231257827021181583404541015625,这是因为0.1无法准确地表示为 double(或者说对于该情况,不能表示为任何有限长度的二进制小数)
这样,传入到构造方法的值不会正好等于 0.1(虽然表面上等于该值)
2)String 构造方法是完全可预知的:写入 newBigDecimal(“0.1”) 将创建一个 BigDecimal,它正好等于预期的 0.1,因此,比较而言, 通常 建议优先使用String构造方法
3)当double必须用作BigDecimal的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:先使用Double.toString(double)方法,然后使用BigDecimal(String)构造方法,将double转换为String。要
获取该结果,请使用 static valueOf(double) 方法
另外,BigDecimal.valueOf() 是静态工厂类,永远优先于构造函数, 所以,在使用的时候, 推荐使用 BigDecimal.valueOf(xxx)
dd