java中的double和BigDecimal,你用对了么?

我们在项目中经常会用到小数,那就来看看常用到的double和BigDecimal有没有用对它俩。

先看代码:

public static void main(String[] args) {
    double d1 = 0.02;
    double d2 = 0.03;
    System.out.println(d2-d1);
}

以你的经验看,会输出什么?0.01?

实际则是:

0.009999999999999998

为什么呢?大概率也都知道:精度问题嘛!

赶个潮流吧,我们让文心一言给解释一下:

OK!在内存中实际上是以最新近的二进制浮点数形式存储的,那么d2-d1也就自然会是一个有误差的结果,是一个近似值。明白了!

再看,它给出了建议方法,用BigDecimal,说BigDecimal适用于高精度计算的场合,恩,这个和我印象中的是一样的,那我们用它来试试看,应该没问题的:)

public static void main(String[] args) {
    BigDecimal b1 = new BigDecimal(0.02);
    BigDecimal b2 = new BigDecimal(0.03);
    System.out.println(b2.subtract(b1));
}

经常这么用吧?用上BigDecimal了,这回应该可以正常的输出0.01了吧?

0.0099999999999999984734433411404097569175064563751220703125

这。是什么操作?怎么还不对?

好吧,稳住,看下new BigDecimal的源代码。

    /**
     * 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> &times; 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);
    }

英文太多,翻译一下:

将一个double类型的值转换为BigDecimal对象,返回的同样是一个近似值!!!

不过,人家也给出了可预测的方法说明:

建议用string参数的构造方法:

    public BigDecimal(String val) {
        this(val.toCharArray(), 0, val.length());
    }

好的,这个好理解,肯定不是近似值了,我们再来试试:

public static void main(String[] args) {
    BigDecimal b1 = new BigDecimal("0.02");
    BigDecimal b2 = new BigDecimal("0.03");
    System.out.println(b2.subtract(b1));
}

输出:

终于是对了!

所以,如果想要使用BigDecimal来用在对精度要求更高的场景,一定要充分了解api才行,并不是用上了就一定是高精度的实现了。

另外一个,关于BigDecimal.valueOf(double val)的使用,也需要注意,主要是0和0.0的区别,这个看看源码中的注解也就清楚了,特别是如果以BigDecimal作为key的场景时,0和0.0重点关注

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值