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

1.前言

1.1.BigDecimal主要做什么?

源码中对其这样描述,

/**
 * An immutable arbitrary-precision signed decimal.
 *
 * 

A value is represented by an arbitrary-precision "unscaled value" and a signed 32-bit "scale", * combined thus: {@code unscaled * 10-scale}. See {@link #unscaledValue} and {@link #scale}. * *

Most operations allow you to supply a {@link MathContext} to specify a desired rounding mode. */ public class BigDecimal extends Number implements Comparable, Serializable {

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

2.分析

2.1.1.使用

BigDecimal做运算,保留小数位四舍五入取舍问题_第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.
     * 

* 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.
     *翻译: 舍入模式,其中正值四舍五入正无穷,负值四舍五入负无穷
     * 
* Rule: {@code x.round().abs() >= x.abs()} */ UP(BigDecimal.ROUND_UP), /** * Rounding mode where the values are rounded towards zero. * * 翻译:四舍五入模式,其中值四舍五入为零 *
* 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}。 *
* 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}。 *
* 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 BigDecimal. */ 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的

4.参考

5.总结

你可能感兴趣的:(Java)