问题描述
在项目开发过程中,经常会使用到金额、百分比等数据的情况。在用代码实现的过程中,就会遇到BigDecimal
的使用。下面我们将着重看下BigDecimal
初始化时的问题。
金额、百分比等不使用Float
和Double
的主要原因是精度问题。可以参考effective java 第209页。
float 和 double 类型主要是为了科学计算和工程计算而设计的,它们执行二进制浮点运算,这是为了在广泛的数值范围上提供较为精确的快速近似计算而精心设计的。
然而,它们并没有提供完全精确的结果,所以不应该被用于需要精确结果的场合 float double 类型尤其不适合用于货币计算 ,因为要让一个 float,double 精确地表示 0.1 (或者 的任何其他负数次方值 )是不可能的。
我们继续讨论BigDecimal
的使用。用以下代码举例:
BigDecimal bd1 = new BigDecimal(1.00):
此时,使用静态代码扫描工具,会出现以下提示:
原因分析:
详细分析:
最主要的原因是由于BigDecimal(double)
存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。
首先,BigDecimal
存在以下4中构造器:
- BigDecimal(int)
- BigDecimal(double)
【不推荐使用】
- BigDecimal(long)
- BigDecimal(String)
【推荐使用】
通过以下代码来测试,BigDecimal(double)
存在精度的问题。
public static void main(String[] args) {
BigDecimal bd1 = new BigDecimal("0.06");
BigDecimal bd2 = new BigDecimal(0.06);
System.out.println(bd1);
System.out.println(bd2);
}
运行结果为:
0.06
0.059999999999999997779553950749686919152736663818359375
很明显已经不是我们想要的结果了。
解决方案:
根据上面的测试,可以使用以下两种方式来构造。
- 将double通过Double.toString(double)先转为String,然后放入BigDecimal的String构造函数中。
- 不通过BigDecimal的构造函数,而是通过它的静态方法BigDecimal.valueOf(double),也同样不会丢失精度。
public static void main(String[] args) {
String string = Double.toString(0.06);
BigDecimal stringBigDecimal = new BigDecimal(string);
BigDecimal bigDecimal = BigDecimal.valueOf(0.06);
System.out.println("stringBigDecimal = " + stringBigDecimal);
System.out.println("bigDecimal = " + bigDecimal);
}
运行结果如下:
stringBigDecimal = 0.06
bigDecimal = 0.06
总结:
BigDecimal
的初始化要使用String入参或者BigDecimal.valueOf();- 浮点数的格式化建议使用
BigDecimal
; - 比较两个
BigDecimal
的value要使用compareTo()。