Java推荐使用BigDecimal进行数值计算,可以避免float,double运算精度。但是需要注意的是在构造BigDecimal对象时,应该使用BigDecimal(String val)字符串作为参数的构造函数或是使用静态方法valueOf(double val)返回BigDecimal对象。
实际场景:
开发过程中商品接口返回商品价格double prdPrice = 99.99d,单位(元),而数据库是以单位(分)整型数据存储的,这里就需要prdPrice * 100计算,下面就可以看下使用不同BigDecimal构造函数的区别。
double prdPrice = 19.99d;
BigDecimal bd100 = new BigDecimal(100);
System.out.println(new BigDecimal(prdPrice).multiply(bd100));
System.out.println(new BigDecimal(Double.toString(prdPrice)).multiply(bd100));
运行结果:
1998.999999999999843680598132777959108352661132812500
1999.00
最后我们对BigDecimal取整,调用intValue方法,一个值是1998分,一个是1999分, 最后存到数据库就有一分钱的差异。
可以看出使用BigDecimal(double val)构造函数的结果是不可预测的,java源码的API里写的比较清楚,如下:
- The results of this constructor can be somewhat unpredictable. One might assume that writing new BigDecimal(0.1) in Java creates a BigDecimal which is exactly equal to 0.1 (an unscaled value of 1, with a scale of 1), but it is actually equal to 0.1000000000000000055511151231257827021181583404541015625. This is because 0.1 cannot be represented exactly as a double (or, for that matter, as a binary fraction of any finite length). Thus, the value that is being passed in to the constructor is not exactly equal to 0.1, appearances notwithstanding.
- The String constructor, on the other hand, is perfectly predictable: writing new BigDecimal("0.1") creates a BigDecimal which is exactly equal to 0.1, as one would expect. Therefore, it is generally recommended that the String constructor be used in preference to this one.
- When a double must be used as a source for a BigDecimal, note that this constructor provides an exact conversion; it does not give the same result as converting the double to a String using the Double.toString(double) method and then using the BigDecimal(String) constructor. To get that result, use the static valueOf(double) method.