double测试代码:
@Test
public void testDouble() {
double d1 = 2.01d;
double d2 = 2.0099999999999997868371792719699442386627197265625d;
System.out.println("d1 == d2 : " + (d1 == d2));
System.out.println("d1 * 100 = " + (d1 * 100));
System.out.println("d2 * 100 = " + (d2 * 100));
}
测试结果
d1 == d2 : true
d1 * 100 = 200.99999999999997
d2 * 100 = 200.99999999999997
说明以double类型保存在计算机里面的浮点数d1=2.01的真实值是d2的值,double类型无法精准表示2.01,声明赋值时,就已经丢失精度了
BigDecimal表示小数:准确的用法是以字符串形式,使用BigDecimal构造函数创建
@Test
public void testBigDecimal() {
double d1 = 2.01d;
String d1Str = "2.01";
System.out.println(new BigDecimal(d1Str).toPlainString());
//与上面以字符串为参数,用构造函数创建是等价的
System.out.println(BigDecimal.valueOf(d1).toPlainString());
//以double类型传入,会得到因double而丢失精度之后的值
System.out.println(new BigDecimal(d1).toPlainString());
}
结果:
2.01
2.01
2.0099999999999997868371792719699442386627197265625
这里之所以要以字符串为BigDecimal构造函数参数,是因为BigDecimal是精确的,如果直接传入double类型参数,会把double类型记录在计算机中的真实值(例如声明 double d1 = 2.01d;此处由于十进制的小数不能用有限长的二进制小数来表示而丢失精度,实际保存的真实值是2.0099999999999997868371792719699442386627197265625,与2.01近似而已,在声明double类型赋值时,精度就丢失了)传给BigDecimal,而我们真正想要的值就是我们看到的值2.01,所以应该直接传入字符串"2.01"。
其他测试
@Test
public void testEquals() {
double d1 = 2.01d;
double d2 = 2.0099999999999997868371792719699442386627197265625d;
System.out.println(d1 == d2);
System.out.println(Double.toString(d1));
System.out.println(Double.toString(d2));
System.out.println(new BigDecimal(d1).equals(BigDecimal.valueOf(d1)));
System.out.println(new BigDecimal(d2).equals(BigDecimal.valueOf(d2)));
System.out.println(new BigDecimal(d1).equals(new BigDecimal(d2)));
System.out.println(BigDecimal.valueOf(d1).equals(BigDecimal.valueOf(d2)));
}
测试结果:
true
2.01
2.01
false
false
true
true
double丢失精度原因:
N 位二进制的小数的精度是 1/2 的 N 次方。也就是说,N 位二进制数只能表示(1/2 的 N 次方)的整数倍的数。M 位十进制的小数的精度是 1/10 的 M 次方。1/10 = 1/2 * 1/5。那么 1/5 这个数(即0.2),就不能用有限长的二进制小数来表示。
参考:
Double和Float运算以及DecimalFormat格式化精度丢失踩坑记录
https://blog.csdn.net/xdkb159/article/details/106081425