BigDecimal做运算,保留小数位四舍五入取舍问题

1.前言

1.1.BigDecimal主要做什么?

源码中对其这样描述,

/**
 * An immutable arbitrary-precision signed decimal.
 *
 * <p>A value is represented by an arbitrary-precision "unscaled value" and a signed 32-bit "scale",
 * combined thus: {@code unscaled * 10<sup>-scale</sup>}. See {@link #unscaledValue} and {@link #scale}.
 *
 * <p>Most operations allow you to supply a {@link MathContext} to specify a desired rounding mode.
 */
public class BigDecimal extends Number implements Comparable<BigDecimal>, Serializable {

“一个不可变的任意精度带符号的小数…”,内部提供了做加减乘除和数值的精度计算

2.分析

2.1.1.使用

在这里插入图片描述
在new创建对象的时候有如上这么多种选择方式传参,这里在计算的时候传入的对象类型不能为double类型,应当用string类型,不是string类型使用String.valueOf()转成string类型。看源码说明

主要看方法注释
 /**
     * Constructs a new {@code BigDecimal} instance from the 64bit double
     * {@code val}. The constructed big decimal is equivalent to the given
     * double. For example, {@code new BigDecimal(0.1)} is equal to {@code
     * 0.1000000000000000055511151231257827021181583404541015625}. This happens
     * as {@code 0.1} cannot be represented exactly in binary.
     * <p>
     * To generate a big decimal instance which is equivalent to {@code 0.1} use
     * the {@code BigDecimal(String)} constructor.
     *
     * @param val
     *            double value to be converted to a {@code BigDecimal} instance.
     * @throws NumberFormatException
     *             if {@code val} is infinity or not a number.
     */
    public BigDecimal(double val) {
        if (Double.isInfinite(val) || Double.isNaN(val)) {
            throw new NumberFormatException("Infinity or NaN: " + val);
        }
        long bits = Double.doubleToLongBits(val); // IEEE-754
        long mantissa;
        int trailingZeros;
        // Extracting the exponent, note that the bias is 1023
        scale = 1075 - (int)((bits >> 52) & 0x7FFL);
        // Extracting the 52 bits of the mantissa.
        mantissa = (scale == 1075) ? (bits & 0xFFFFFFFFFFFFFL) << 1
                : (bits & 0xFFFFFFFFFFFFFL) | 0x10000000000000L;
        if (mantissa == 0) {
            scale = 0;
            precision = 1;
        }
        // To simplify all factors '2' in the mantissa
        if (scale > 0) {
            trailingZeros = Math.min(scale, Long.numberOfTrailingZeros(mantissa));
            mantissa >>>= trailingZeros;
            scale -= trailingZeros;
        }
        // Calculating the new unscaled value and the new scale
        if((bits >> 63) != 0) {
            mantissa = -mantissa;
        }
        int mantissaBits = bitLength(mantissa);
        if (scale < 0) {
            bitLength = mantissaBits == 0 ? 0 : mantissaBits - scale;
            if(bitLength < 64) {
                smallValue = mantissa << (-scale);
            } else {
                BigInt bi = new BigInt();
                bi.putLongInt(mantissa);
                bi.shift(-scale);
                intVal = new BigInteger(bi);
            }
            scale = 0;
        } else if (scale > 0) {
            // m * 2^e =  (m * 5^(-e)) * 10^e
            if(scale < LONG_FIVE_POW.length
                    && mantissaBits+LONG_FIVE_POW_BIT_LENGTH[scale] < 64) {
                smallValue = mantissa * LONG_FIVE_POW[scale];
                bitLength = bitLength(smallValue);
            } else {
                setUnscaledValue(Multiplication.multiplyByFivePow(BigInteger.valueOf(mantissa), scale));
            }
        } else { // scale == 0
            smallValue = mantissa;
            bitLength = mantissaBits;
        }
    }

注释说因为double类型的数不能用二进制精确的表示,所以将数值转换为string做十进制运算。

2.2.1.加法运算
BigDecimal number1 = new BigDecimal("11");
BigDecimal number2 = new BigDecimal("13.5");
BigDecimal add = number1.add(number2);
double v = add.doubleValue();
System.out.println("add = "+v);

结果为"add = 24.5"

2.3.1.减法运算
BigDecimal number1 = new BigDecimal("11");
BigDecimal number2 = new BigDecimal("13.5");
BigDecimal add = number1.subtract(number2);
double v = add.doubleValue();
System.out.println("subtract = "+v);

结果为"subtract = -2.5"

2.4.1.乘法运算
BigDecimal number1 = new BigDecimal("11");
BigDecimal number2 = new BigDecimal("13.5");
BigDecimal add = number1.multiply(number2);
double v = add.doubleValue();
System.out.println("multiply = "+v);

结果为"multiply = 148.5"

2.5.1.除法运算
BigDecimal number1 = new BigDecimal("10");
BigDecimal number2 = new BigDecimal("3");
BigDecimal add = number1.divide(number2,5,RoundingMode.DOWN);
double v = add.doubleValue();
System.out.println("divide = "+v);
// 这里解释一下number1.divide()方法的3个参数,
// 第一个参数是除数,
// 第二个参数是如果除不完保留几位小数,
// 最后一个参数是舍入模式,RoundingMode.DOWN为第二个参数后面的位数全部都舍去,不做四舍五入。

结果为“divide = 3.33333”

2.6.1.RoundingMode的几种模式

以下是源码RoundingMode类

/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

package java.math;

/**
 * Specifies the rounding behavior for operations whose results cannot be
 * represented exactly.
 */
public enum RoundingMode {

    /**
     * Rounding mode where positive values are rounded towards positive infinity
     * and negative values towards negative infinity.
     *翻译: 舍入模式,其中正值四舍五入正无穷,负值四舍五入负无穷
     * <br>
     * Rule: {@code x.round().abs() >= x.abs()}
     */
    UP(BigDecimal.ROUND_UP),

    /**
     * Rounding mode where the values are rounded towards zero.
     *  * 翻译:四舍五入模式,其中值四舍五入为零
     * <br>
     * Rule: {@code x.round().abs() <= x.abs()}
     */
    DOWN(BigDecimal.ROUND_DOWN),

    /**
     * Rounding mode to round towards positive infinity. For positive values
     * this rounding mode behaves as {@link #UP}, for negative values as
     * {@link #DOWN}.
     * 翻译:四舍五入模式向正无穷四舍五入。对于正值,这种舍入模式表现为{@link #UP},对于负值,则表现为{@link #DOWN}。
     * <br>
     * Rule: {@code x.round() >= x}
     */
    CEILING(BigDecimal.ROUND_CEILING),

    /**
     * Rounding mode to round towards negative infinity. For positive values
     * this rounding mode behaves as {@link #DOWN}, for negative values as
     * {@link #UP}.
     * 翻译:四舍五入模式向负无穷四舍五入。对于正值,这种舍入模式表现为{@link #DOWN},对于负值,则表现为{@link #UP}。
     * <br>
     * Rule: {@code x.round() <= x}
     */
    FLOOR(BigDecimal.ROUND_FLOOR),

    /**
     * Rounding mode where values are rounded towards the nearest neighbor. Ties
     * are broken by rounding up.
     * 翻译:舍入模式,将值舍入到最接近的邻居。 领带被四舍五入打破
     */
    HALF_UP(BigDecimal.ROUND_HALF_UP),

    /**
     * Rounding mode where values are rounded towards the nearest neighbor. Ties
     * are broken by rounding down.
     * 翻译:舍入模式,将值舍入到最接近的邻居。 四舍五入关系被打破
     */
    HALF_DOWN(BigDecimal.ROUND_HALF_DOWN),

    /**
     * Rounding mode where values are rounded towards the nearest neighbor. Ties
     * are broken by rounding to the even neighbor.
     * 翻译:舍入模式,将值舍入到最接近的邻居。 通过四舍五入来打平关系
     */
    HALF_EVEN(BigDecimal.ROUND_HALF_EVEN),

    /**
     * Rounding mode where the rounding operations throws an ArithmeticException
     * for the case that rounding is necessary, i.e. for the case that the value
     * cannot be represented exactly.
     * 翻译:舍入模式,在需要舍入的情况下,即在无法精确表示值的情况下,舍入操作会引发ArithmeticException
     */
    UNNECESSARY(BigDecimal.ROUND_UNNECESSARY);

    /** The old constant of <code>BigDecimal</code>. */
    private final int bigDecimalRM;

    /** It sets the old constant. */
    RoundingMode(int rm) {
        bigDecimalRM = rm;
    }

    /**
     * Converts rounding mode constants from class {@code BigDecimal} into
     * {@code RoundingMode} values.
     *
     * @param mode
     *            rounding mode constant as defined in class {@code BigDecimal}
     * @return corresponding rounding mode object
     */
    public static RoundingMode valueOf(int mode) {
        switch (mode) {
            case BigDecimal.ROUND_CEILING:
                return CEILING;
            case BigDecimal.ROUND_DOWN:
                return DOWN;
            case BigDecimal.ROUND_FLOOR:
                return FLOOR;
            case BigDecimal.ROUND_HALF_DOWN:
                return HALF_DOWN;
            case BigDecimal.ROUND_HALF_EVEN:
                return HALF_EVEN;
            case BigDecimal.ROUND_HALF_UP:
                return HALF_UP;
            case BigDecimal.ROUND_UNNECESSARY:
                return UNNECESSARY;
            case BigDecimal.ROUND_UP:
                return UP;
            default:
                throw new IllegalArgumentException("Invalid rounding mode");
        }
    }
}

第三个参数指定模式RoundingMode.UP也使用这种写法BigDecimal.ROUND_UP

number1.divide(number2,5,BigDecimal.ROUND_UP);
//BigDecimal.ROUND_DOWN;
//BigDecimal.ROUND_UP;
//BigDecimal.ROUND_CEILING;
//BigDecimal.ROUND_FLOOR;
//BigDecimal.ROUND_HALF_DOWN;
//BigDecimal.ROUND_HALF_EVEN;
//BigDecimal.ROUND_HALF_UP;
//BigDecimal.ROUND_UNNECESSARY;

//源码
...
 public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode) {
        return divide(divisor, scale, RoundingMode.valueOf(roundingMode));
    }
...
//所以传入BigDecimal.ROUND_UP还是被转成了RoundingMode.UP
2.7.1.精度值计算
double value = new BigDecimal(String.valueOf(13.987)).setScale(2, BigDecimal.ROUND_UP).doubleValue();
DecimalFormat format = new DecimalFormat("#0.00");
System.out.println("decimal = " + format.format(value));
//结果为“decimal = 13.99”
//模式上面已经有说明可根据模式选择取舍
//创建DecimalFormat传入"#0.00"参数是因为想要保留两位小数,
//因为如果一个数的小数位如果是0的话,BigDecimal是不展示小数位0的

3.最后

开通了个公众号,扫码关注一下,可以获得超过1个G的免费PDF书籍学习资料,并且可以及时收到我分享的内容哦!
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值