为什么使用add2
方法的结果是错误的?Double.toString(d1)
为什么能保持double
的精度不丢失?
public static double add(double d1, double d2) {
BigDecimal bd1 = new BigDecimal(Double.toString(d1));
BigDecimal bd2 = new BigDecimal(Double.toString(d2));
return bd1.add(bd2).doubleValue();
}
public static double add2(double d1, double d2) {
BigDecimal bd1 = new BigDecimal(d1);
BigDecimal bd2 = new BigDecimal(d2);
return bd1.add(bd2).doubleValue();
}
public static void main(String[] args) {
double d1 = 1.0000002;
double d2 = 0.0000002;
System.out.println(Double.toString(d1));
System.out.println("d1 + d2 = " + (d1 + d2)); // 1.0000003999999998
System.out.println("d1 + d2 = " + add(d1, d2)); // 1.0000004
System.out.println("d1 + d2 = " + add2(d1, d2)); // 1.0000003999999998
}
PS:引出一个概念问题:
double d = XXXX.XXXXXX;
System.out.println(d); //这里总是不会丢失精度的,为什么?
When BigDecimal uses double as input parameter, binary cannot accurately represent decimals. After the compiler reads the strings "0.0000002" and "1.0000002", it must convert it into an 8-byte double value, which is
1.0000001999999998947288304407265968620777130126953125
类似这种。所以,运行的时候,实际传给BigDecimal构造函数的真正的数值是
1.0000001999999998947288304407265968620777130126953125
.BigDecimal can correctly convert the string into a truly accurate floating point number when using String as an input parameter.
System.out.println part, if the input parameter is string, it will be output directly. If the input parameter is other types, then the Object.toString method will be called to convert and then output. Double.toString will use a certain precision to round the double before outputting it.
The comment on the BigDecimal constructor writes this question:
Additional explanation:
Double.toString
This method outputs aString
, and it will be rounded.Double.toString
这个方法输出的是一个String
,而且会进行四舍五入处理。new BigDecimal(Double.toString(d1))
这个入参在处理完毕之后是一个String
,调用的是BigDecimal(String val)
new BigDecimal(Double.toString(d1))
After processing, this input parameter is aString
, and the call isBigDecimal(String val)
This construction method.The BigDecimal(String val) method in the source code will process val into a char[] array:
Then call
BigDecimal(char[] in)
this constructor.And new BigDecimal(d1) calls
BigDecimal(double val)
. The first thing after this method comes in isConvert the input parameters into binary, so accuracy will be lost.
The loss of precision caused by double addition is the same as the above situation. It is first converted into bits and then calculated, and then the precision is lost. .