BigDecimal为什么能保证精度不丢失?
先说说为什么会丢精度。
public static void main(String[] args) { //正常 3.3 System.out.println("加法结果:"+(1.1+2.2)); //正常 -7.9 System.out.println("减法结果:"+(2.2-10.1)); //正常 2.42 System.out.println("乘法结果:"+(1.1*2.2)); //正常 0.44 System.out.println("除法结果:"+(4.4/10)); }
结果:
为什么会丢失精度?
在于我们的计算机是二进制的。十进制小数没有办法是用二进制进行精确表示。
浮点数的值实际上是由一个特定的数学公式计算得到的。
// 0.2 转换为二进制数的过程为,不断乘以 2,直到不存在小数为止, // 在这个计算过程中,得到的整数部分从上到下排列就是二进制的结果。 0.2 * 2 = 0.4 -> 0 0.4 * 2 = 0.8 -> 0 0.8 * 2 = 1.6 -> 1 0.6 * 2 = 1.2 -> 1 0.2 * 2 = 0.4 -> 0(发生循环)
0.4 * 2 = 0.8 -> 0 0.8 * 2 = 1.6 -> 1 0.6 * 2 = 1.2 -> 1
...
所以二进制表示为:0.001100110011001100110011001100110011001100110011001101...
循环根为:0011
但是十进制的整数转换为二进制没有问题,那么把十进制小数扩大N倍让它在整数的维度上进行计算,并保留相应的精度信息,这就是BigDecimal的做法。
BigDecimal
scale表示精度信息,intCompact表示整数信息。
因此,BigDecimal 表示的数值是 (intCompact × 10-scale)。
四种构造函数
BigDecimal(int) //创建一个具有参数所指定整数值的对象。 BigDecimal(double) //创建一个具有参数所指定双精度值的对象。 BigDecimal(long) //创建一个具有参数所指定长整数值的对象。 BigDecimal(String) //创建一个具有参数所指定以字符串表示的数值的对象。
这几个都是常用的构造器,他们返回的对象都是BigDecimal对象。换而言之,将BigDecimal对象转换为其他类型的对象,我们通过以下几种。
toString() //将BigDecimal对象的数值转换成字符串。 doubleValue() //将BigDecimal对象中的值以双精度数返回。 floatValue() //将BigDecimal对象中的值以单精度数返回。 longValue() //将BigDecimal对象中的值以长整数返回。 intValue() //将BigDecimal对象中的值以整数返回。
这里需要非常注意BigDecimal(double)的构造函数,也是会存在精度丢失的问题。
public static void main(String[] args) { BigDecimal intDecimal = new BigDecimal(10); BigDecimal doubleDecimal = new BigDecimal(4.3); BigDecimal longDecimal = new BigDecimal(10L); BigDecimal stringDecimal = new BigDecimal("4.3"); System.out.println("intDecimal=" + intDecimal); System.out.println("doubleDecimal=" + doubleDecimal); System.out.println("longDecimal=" + longDecimal); System.out.println("stringDecimal=" + stringDecimal); }
结果:
所以创建BigDecimal一定要使用下面两种方式:
1、new BigDecimal(String) 2、BigDecimal.valueOf(double)
BigDecimal 等值比较问题
《阿里巴巴 Java 开发手册》中提到:
BigDecimal
使用 equals()
方法进行等值比较出现问题的代码示例:
这是因为 equals()
方法不仅仅会比较值的大小(value)还会比较精度(scale),而 compareTo()
方法比较的时候会忽略精度。
1.0 的 scale 是 1,1 的 scale 是 0,因此 a.equals(b)
的结果是 false。