错误示例
日常开发中,我们都会选择BigDecimal进行针对小数的运算以获得精准的结果,但是有时即使使用了BigDecimal,得出来的结果依然不精准。
如:
public static void main(String[] args) {
BigDecimal subVal = new BigDecimal(1.3).subtract(new BigDecimal(1));
System.out.println(subVal.doubleValue());
}
运算结果为:0.30000000000000004
并没有得到期望中的结果:0.3
产生原因
这是因为new BigDecimal(1.3)这个构造方法上:在JDKBigDecimal(double val)
这个构造方法的注释中写道:
* <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>
即使用BigDecimal(double val)得出的结果可能有点不可预测,这是因为double的本身并不精准导致的,如你入参传入一个0.1,但是这个0.1在内存中实际的值为0.10000000000000055115123125782702118158340541015625,BigDecimal会拿实际值进行精准计算,因此会得出一个开发者预料之外的结果。
解决方案
解决方案在注释中也已经提出:使用BigDecimal(String val)
即字符串形式的入参构造,如果要进行double的运算,则建议开发者使用Double.toString(double)将double转换为字符串再进行构造。
public static void main(String[] args) {
BigDecimal subVal = new BigDecimal("1.3").subtract(new BigDecimal("1"));
System.out.println(subVal.doubleValue());
}
运算结果为:0.3
符合预期