【小笔记】为什么使用BigDecimal计算出来的结果依然不精准

错误示例

日常开发中,我们都会选择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 符合预期

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yue_hu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值