当需要进行高精度计算时,比如银行系统的money结算,一般都会采用BigDecimal来进行计算,这已经是一个常识了。但是BigDecimal一定不会丢失精度吗?
先看一个例子:
public class Test {
public static void main(String[] args) {
BigDecimal value1 = new BigDecimal(0.1);
BigDecimal value2 = new BigDecimal(0.2);
System.out.println((value1.add(value2)));
}
}
输出:
0.3000000000000000166533453693773481063544750213623046875
Process finished with exit code 0
怎么样,是不是很意外?
看下底层注释
/**
* Translates a {@code double} into a {@code BigDecimal} which
* is the exact decimal representation of the {@code double}'s
* binary floating-point value. The scale of the returned
* {@code BigDecimal} is the smallest value such that
* <tt>(10<sup>scale</sup> × val)</tt> is an integer.
* <p>
* <b>Notes:</b>
* <ol>
* <li>
* The results of this constructor can be somewhat unpredictable.
* One might assume that writing {@code new BigDecimal(0.1)} in
* Java creates a {@code 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
* {@code double} (or, for that matter, as a binary fraction of
* any finite length). Thus, the value that is being passed
* <i>in</i> to the constructor is not exactly equal to 0.1,
* appearances notwithstanding.
*
* <li>
* The {@code String} constructor, on the other hand, is
* perfectly predictable: writing {@code new BigDecimal("0.1")}
* creates a {@code BigDecimal} which is <i>exactly</i> equal to
* 0.1, as one would expect. Therefore, it is generally
* recommended that the {@linkplain #BigDecimal(String)
* <tt>String</tt> constructor} be used in preference to this one.
*
* <li>
* When a {@code double} must be used as a source for a
* {@code BigDecimal}, note that this constructor provides an
* exact conversion; it does not give the same result as
* converting the {@code double} to a {@code String} using the
* {@link Double#toString(double)} method and then using the
* {@link #BigDecimal(String)} constructor. To get that result,
* use the {@code static} {@link #valueOf(double)} method.
* </ol>
*
* @param val {@code double} value to be converted to
* {@code BigDecimal}.
* @throws NumberFormatException if {@code val} is infinite or NaN.
*/
public BigDecimal(double val) {
this(val,MathContext.UNLIMITED);
}
注释里面有说明,如果构造了0.1,其实只能无限接近这个值,但不能完全准确。如果要准确地赋值的话,需要用到输入参数类型为字符串的构造器。
public class Test {
public static void main(String[] args) {
BigDecimal value1 = new BigDecimal("0.1");
BigDecimal value2 = new BigDecimal("0.2");
System.out.println((value1.add(value2)));
}
}
输出:
0.3
Process finished with exit code 0
可见,当构造BigDecimal对象时,如果传入的参数不是字符串类型,则计算精度可能出问题。只有构造参数类型为字符串时,才会完全准确。