Java源码分析系列之BigDecimal

本文详细介绍了Java中BigDecimal类的内部结构和工作原理,包括其不可变的、任意精度的十进制数特性,以及如何进行算术、标度、舍入等操作。还探讨了BigDecimal的精度控制、舍入模式以及与MathContext的关系,揭示了其在处理高精度计算时的关键细节。
摘要由CSDN通过智能技术生成

/**
 * 不可变的、任意精度的有符号十进制数。BigDecimal 由任意精度的整数非标度值 和 32 位的整数标度 (scale) 组成。
 * BigDecimal 类提供以下操作:算术、标度操作、舍入、比较、哈希算法和格式转换
 * BigDecimal 类使用户能完全控制舍入行为。如果未指定舍入模式,并且无法表示准确结果,则抛出一个异常;
 * 否则,通过向该操作提供适当的 MathContext 对象,可以对已选择的精度和舍入模式执行计算。
 * 在任何情况下,可以为舍入控制提供八种舍入模式。使用此类(例如,ROUND_HALF_UP)中的整数字段来表示舍入模式已过时;
 * 应改为使用 RoundingMode enum(例如,RoundingMode.HALF_UP)的枚举值。 
 *在除法中,准确的商可能是一个无限长的十进制扩展;
 * 例如,1 除以 3 所得的商。如果商具有无穷的十进制扩展,但是指定了该操作返回准确结果,则抛出 ArithmeticException
 */
public class BigDecimal extends Number implements Comparable<BigDecimal> {
    /*
     * 继承Number类提供将表示的数值转换为 byte、double、float、int、long 和 short 的方法。<br />
     * 实现Comparable接口,获取到compareTo方法。
     */
    // BigDecimal的未scale的值,BigInteger是一个任意长度的整数(整数非标度值)
    private volatile BigInteger intVal;
 
    // BigDecimal的标度(小数点),输入数除以10的scale次幂(32 位的整数标度)
    private int scale; // 注意:这可能有任何值,因此计算必须长时间进行
    // BigDecimal的精度(精度是非标度值的数字个数。)
    private transient int precision;
 
    // toString后缓存
    private transient String stringCache;
    // 标记值为intCompact表示有效数字信息只能从intVal中获得。
    static final long INFLATED = Long.MIN_VALUE;
    // 若BigDecimal的绝对值小于Long.MAX_VALUE,放在这个变量中
    private transient long intCompact;
    /*
     * 所有18位基数的10个字符串组成一个长字符串;
     * 不是所有的19位字符串都可以
     */
    private static final int MAX_COMPACT_DIGITS = 18;
 
    private static final int MAX_BIGINT_BITS = 62;
    // 序列化
    private static final long serialVersionUID = 6108874887143696463L;
 
    private static final ThreadLocal<StringBuilderHelper>
        threadLocalStringBuilderHelper = new ThreadLocal<StringBuilderHelper>() {
        @Override
        protected StringBuilderHelper initialValue() {
            return new StringBuilderHelper();
        }
    };
 
  //缓存0 ~ 10
    private static final BigDecimal zeroThroughTen[] = {
        new BigDecimal(BigInteger.ZERO,         0,  0, 1),
        new BigDecimal(BigInteger.ONE,          1,  0, 1),
        new BigDecimal(BigInteger.valueOf(2),   2,  0, 1),
        new BigDecimal(BigInteger.valueOf(3),   3,  0, 1),
        new BigDecimal(BigInteger.valueOf(4),   4,  0, 1),
        new BigDecimal(BigInteger.valueOf(5),   5,  0, 1),
        new BigDecimal(BigInteger.valueOf(6),   6,  0, 1),
        new BigDecimal(BigInteger.valueOf(7),   7,  0, 1),
        new BigDecimal(BigInteger.valueOf(8),   8,  0, 1),
        new BigDecimal(BigInteger.valueOf(9),   9,  0, 1),
        new BigDecimal(BigInteger.TEN,          10, 0, 2),
    };
 
  //缓存0 ~ 0E-15
    private static final BigDecimal[] ZERO_SCALED_BY = {
        zeroThroughTen[0],
        new BigDecimal(BigInteger.ZERO, 0, 1, 1),
        new BigDecimal(BigInteger.ZERO, 0, 2, 1),
        new BigDecimal(BigInteger.ZERO, 0, 3, 1),
        new BigDecimal(BigInteger.ZERO, 0, 4, 1),
        new BigDecimal(BigInteger.ZERO, 0, 5, 1),
        new BigDecimal(BigInteger.ZERO, 0, 6, 1),
        new BigDecimal(BigInteger.ZERO, 0, 7, 1),
        new BigDecimal(BigInteger.ZERO, 0, 8, 1),
        new BigDecimal(BigInteger.ZERO, 0, 9, 1),
        new BigDecimal(BigInteger.ZERO, 0, 10, 1),
        new BigDecimal(BigInteger.ZERO, 0, 11, 1),
        new BigDecimal(BigInteger.ZERO, 0, 12, 1),
        new BigDecimal(BigInteger.ZERO, 0, 13, 1),
        new BigDecimal(BigInteger.ZERO, 0, 14, 1),
        new BigDecimal(BigInteger.ZERO, 0, 15, 1),
    };
    // Long的一半。MIN_VALUE & Long.MAX_VALUE。
    private static final long HALF_LONG_MAX_VALUE = Long.MAX_VALUE / 2;
    private static final long HALF_LONG_MIN_VALUE = Long.MIN_VALUE / 2;
 
    // 常量
    // 值为 0,标度为 0。 
    public static final BigDecimal ZERO =
        zeroThroughTen[0];
 
    // 值为 1,标度为 0。
    public static final BigDecimal ONE =
        zeroThroughTen[1];
 
    // 值为 10,标度为 0。
    public static final BigDecimal TEN =
        zeroThroughTen[10];
 
    // 构造函数
    /**
     * 将 BigDecimal 的字符数组表示形式转换为 BigDecimal,接受与 BigDecimal(String) 构造方法相同的字符序列,同时允许指定子数组,并根据上下文设置进行舍入。 
     * 注意,如果字符数组中已经提供字符的序列,则使用此构造方法要比将 char 数组转换为字符串并使用 BigDecimal(String) 构造方法更快。
     */
    public BigDecimal(char[] in, int offset, int len, MathContext mc) {
        this(in, offset, len);
        if (mc.precision > 0) // 精度大于0
            roundThis(mc);  // 根据MathContext设置将这个BigDecimal四舍五入
    }
    public BigDecimal(char[] in) {
        this(in, 0, in.length);
    }
    public BigDecimal(char[] in, MathContext mc) {
        this(in, 0, in.length, mc);
    }
 
    /**
     * 将 BigDecimal 的字符串表示形式转换为 BigDecimal。
     * 这通常是将 float 或 double 转换为 BigDecimal 的首选方法因为它不会遇到 BigDecimal(double) 构造方法的不可预知问题。 
     */
    public BigDecimal(String val) {
        this(val.toCharArray(), 0, val.length());
    }
 
    /**
     * 将 BigDecimal 的字符串表示形式转换为 BigDecimal,接受与 BigDecimal(String) 构造方法相同的字符串(按照上下文设置进行舍入)。 
     */
    public BigDecimal(String val, MathContext mc) {
        this(val.toCharArray(), 0, val.length());
        if (mc.precision > 0)// 精度大于0
            roundThis(mc);// 根据MathContext设置将这个BigDecimal四舍五入
    }
 
    /**
     * 将 double 转换为 BigDecimal,后者是 double 的二进制浮点值准确的十进制表示形式。
     * 2、String 构造方法是完全可预知的:写入 new BigDecimal("0.1") 将创建一个 BigDecimal,它正好 等于预期的 0.1。建议优先使用 String 构造方法。 
     * 3、当 double 必须用作 BigDecimal 的源时,请注意,此构造方法提供了一个准确转换;它不提供与以下操作相同的结果:
     * 先使用 Double.toString(double) 方法,然后使用 BigDecimal(String) 构造方法,将 double 转换为 String。要获取该结果,请使用 static valueOf(double) 方法。 
     */
    public BigDecimal(double val) {
        if (Double.isInfinite(val) || Double.isNaN(val)) // 如果 val 为无穷大或 NaN
            throw new NumberFormatException("Infinite or NaN");
        // 将double转化为符号、exponent和significand
        /*
         * 根据 IEEE 754 浮点双精度格式 ("double format") 位布局,返回val的表示形式。
         * 第 63 位(掩码 0x8000000000000000L 选定的位)表示浮点数的符号。
         * 第 62-52 位(掩码 0x7ff0000000000000L 选定的位)表示指数。
         * 第 51-0 位(掩码 0x000fffffffffffffL 选定的位)表示浮点数的有效数字(有时也称为尾数)。 
         */
        long valBits = Double.doubleToLongBits(val);
        int sign = ((valBits >> 63)==0 ? 1 : -1);// 右移63位为符号,判断是0还是1
        int exponent = (int) ((valBits >> 52) & 0x7ffL);// 右移52位指数
        long significand = (exponent==0 ? (valBits & ((1L<<52) - 1)) << 1
                            : (valBits & ((1L<<52) - 1)) | (1L<<52));
        exponent -= 1075;
        // 这时候, val == sign * significand * 2**exponent.
        /*
         * 特殊情况零可以抑制无尽的规范化和虚假的规模计算。
         */
        if (significand == 0) {
            intVal = BigInteger.ZERO;
            intCompact = 0;
            precision = 1;
            return;
        }
 
        // 正常化
        while((significand & 1) == 0) {    //  i.e., significand 是偶数,significand等于0;&计算:两位同为1结果为1,否则为0
            significand >>= 1;
            exponent++;
        }
        // 计算intVal和scale
        long s = sign * significand;
        BigInteger b;
        if (exponent < 0) {// 指数小于0
            // 5 的-exponent次幂乘以s
            b = BigInteger.valueOf(5).pow(-exponent).multiply(s);
            scale = -exponent;
        } else if (exponent > 0) {// 指数大于0
            // 2 的exponent次幂乘以s
            b = BigInteger.valueOf(2).pow(exponent).multiply(s);
        } else {
            b = BigInteger.valueOf(s);// 获取s的 BigInteger值
        }
        intCompact = compactValFor(b);// 获取val(BigInteger)的compact值。
        intVal = (intCompact != INFLATED) ? null : b;
    }
 
    /**
     * 将 double 转换为 BigDecimal(根据上下文设置进行舍入)。BigDecimal 的标度是使 (10的scale次幂 × val) 为整数的最小值。 
     * 此构造方法的结果有一定的不可预知性,通常不建议使用它,请参见 BigDecimal(double) 构造方法下面的注释。 
     * @param val    要转换为 BigDecimal 的 double 值
     * @param mc    要使用的上下文
     */
    public BigDecimal(double val, MathContext mc) {
        this(val);
        if (mc.precision > 0)// 精度大于0
            roundThis(mc);// 根据MathContext设置将这个BigDecimal四舍五入
    }
 
    /**
     * 将 BigInteger 转换为 BigDecimal。BigDecimal 的标度是零
     * @param val    要转换为 BigDecimal 的 BigInteger 值
     */
    public BigDecimal(BigInteger val) {
        intCompact = compactValFor(val);// 获取val(BigInteger)的compact值。
        intVal = (intCompact != INFLATED) ? null : val;
    }
    /**
     * 将 BigInteger 转换为 BigDecimal(根据上下文设置进行舍入)。BigDecimal 的标度为零。
     * @param val    要转换为 BigDecimal 的 BigInteger 值。
     * @param mc    要使用的上下文。 
     */
    public BigDecimal(BigInteger val, MathContext mc) {
        this(val);
        if (mc.precision > 0)// 精度大于0
            roundThis(mc);// 根据MathContext设置将这个BigDecimal四舍五入
    }
    /**
     * 将 BigInteger 非标度值和 int 标度转换为 BigDecimal。BigDecimal 的值为 (unscaledVal × 10的-scale幂)。 
     * @param unscaledVal    BigDecimal 的非标度值
     * @param scale    BigDecimal 的标度
     */
    public BigDecimal(BigInteger unscaledVal, int scale) {
        // 现在允许负标度
        this(unscaledVal);
        this.scale = scale;
    }
 
    /**
     * 将 BigInteger 非标度值和 int 标度转换为 BigDecimal(根据上下文设置进行舍入)。
     * BigDecimal 的值为 (unscaledVal × 10-scale),它是根据 precision 和舍入模式设置进行舍入的。 
     * @param unscaledVal    BigDecimal 的非标度值
     * @param scale    BigDecimal 的标度
     * @param mc    要使用的上下文
     */
    public BigDecimal(BigInteger unscaledVal, int scale, MathContext mc) {
        this(unscaledVal);
        this.scale = scale;
        if (mc.precision > 0) // 精度大于0
            roundThis(mc);// 根据MathContext设置将这个BigDecimal四舍五入
    }
 
    /**
     * 将 int 转换为 BigDecimal。BigDecimal 的标度为零
     * @param val 要转换为 BigDecimal 的 int 值
     */
    public BigDecimal(int val) {
        intCompact = val;
    }
 
    /**
     * 将 int 转换为 BigDecimal(根据上下文设置进行舍入)。在进行任何舍入之前,BigDecimal 的标度为零
     * @param val    要转换为 BigDecimal 的 int 值
     * @param mc    要使用的上下文
     */
    public BigDecimal(int val, MathContext mc) {
        intCompact = val;
        if (mc.precision > 0)
            roundThis(mc);// 根据MathContext设置将这个BigDecimal四舍五入
    }
 
    /**
     * 将 long 转换为 BigDecimal。BigDecimal 的标度为零。 
     * @param val    要转换为 BigDecimal 的 long 值
     */
    public BigDecimal(long val) {
        this.intCompact = val;
        this.intVal = (val == INFLATED) ? BigInteger.valueOf(val) : null;
    }
 
    /**
     * 将 long 转换为 BigDecimal(根据上下文设置进行舍入)。在进行任何舍入之前,BigDecimal 的标度为零。 
     * @param val    要转换为 BigDecimal 的 long 值
     * @param mc    要使用的上下文。
     */
    public BigDecimal(long val, MathContext mc) {
        this(val);
        if (mc.precision > 0)
            roundThis(mc);// 根据MathContext设置将这个BigDecimal四舍五入
    }
 
    // 静态工厂方法
 
    /**
     * 将 long 非标度值和 int 标度转换为 BigDecimal。提供的此“静态工厂方法”优先于 (long, int) 构造方法,因为前者允许重用经常使用的 BigDecimal 值。 
     * @param unscaledVal    BigDecimal 的非标度值
     * @param scale    BigDecimal 的标度
     * @return    其值为 (unscaledVal × 10的-scale次幂) 的 BigDecimal
     */
    public static BigDecimal valueOf(long unscaledVal, int scale) {
        if (scale == 0)// 标度为0,将 long 值转换为具有零标度的 BigDecimal。
            return valueOf(unscaledVal);
        else if (unscaledVal == 0) {
            if (scale > 0 && scale < ZERO_SCALED_BY.length)// 不超过缓存,直接返回缓存
                return ZERO_SCALED_BY[scale];
            else
                return new BigDecimal(BigInteger.ZERO, 0, scale, 1);
        }
        return new BigDecimal(unscaledVal == INFLATED ?
                              BigInteger.valueOf(unscaledVal) : null,
                              unscaledVal, scale, 0);
    }
    /**
     * 将 long 值转换为具有零标度的 BigDecimal。提供的此“静态工厂方法”优先于 (long) 构造方法,因为前者允许重用经常使用的 BigDecimal 值。 
     * @param val    BigDecimal 的值
     * @return    其值为 val 的 BigDecimal
     */
    public static BigDecimal valueOf(long val) {
        if (val >= 0 && val < zeroThroughTen.length)// 不超过缓存,直接返回缓存
            return zeroThroughTen[(int)val];
        else if (val != INFLATED)
            return new BigDecimal(null, val, 0, 0);
        return new BigDecimal(BigInteger.valueOf(val), val, 0, 0);
    }
    /**
     * 使用 Double.toString(double) 方法提供的 double 规范的字符串表示形式将 double 转换为 BigDecimal。 
     * 注:这通常是将 double(或 float)转化为 BigDecimal 的首选方法,因为返回的值等于从构造 BigDecimal(使用 Double.toString(double) 得到的结果)得到的值。 
     * @param val    要转换为 BigDecimal 的 double。 
     * @return    其值等于或约等于 val 值的 BigDecimal。 
     */
    public static BigDecimal valueOf(double val) {
        // 提醒:一个zero double返回“0.0”,因此我们不能快速路径来使用常量zero。这一点可能非常重要,足以在以后证明工厂方法、缓存或一些私有常量是合理的。
        return new BigDecimal(Double.toString(val));
    }
 
    // 算术运算
    /**
     * 返回一个 BigDecimal,其值为 (this + augend),其标度为 max(this.scale(), augend.scale())。 
     * @param augend    将添加到此 BigDecimal 中的值。
     * @return    this + augend
     */
    public BigDecimal add(BigDecimal augend) {
        long xs = this.intCompact;// 整型数字表示的BigDecimal
        long ys = augend.intCompact;// 整型数字表示的BigDecimal
        //初始化        BigInteger的值,intVal为BigDecimal的一个BigInteger类型的属性
        BigInteger fst = (xs != INFLATED) ? null : this.intVal;
        BigInteger snd = (ys != INFLATED) ? null : augend.intVal;
        int rscale = this.scale;
 
        long sdiff = (long)rscale - augend.scale;//小数位数之差
        if (sdiff != 0) {// 取小数位数多的为结果的小数位数
            if (sdiff < 0) {// 当前小数位数小于augend的小数位数
                int raise = checkScale(-sdiff);// 验证-sdiff为int
                rscale = augend.scale;
                // 计算xs * 10 ^ raise == INFLATED
                if (xs == INFLATED ||
                    (xs = longMultiplyPowerTen(xs, raise)) == INFLATED)
                    // 计算this* 10 ^ raise
                    fst = bigMultiplyPowerTen(raise);
            } else {// 当前小数位数大于等于augend的小数位数
                int raise = augend.checkScale(sdiff);// 取当前值
                // 计算ys * 10 ^ raise == INFLATED
                if (ys == INFLATED ||
                    (ys = longMultiplyPowerTen(ys, raise)) == INFLATED)
                    // 计算this* 10 ^ raise
                    snd = augend.bigMultiplyPowerTen(raise);
            }
        }
        if (xs != INFLATED && ys != INFLATED) {
            long sum = xs + ys;
            if ( (((sum ^ xs) & (sum ^ ys))) >= 0L)//判断有无溢出
                return BigDecimal.valueOf(sum, rscale);//返回使用BigDecimal的静态工厂方法得到的BigDecimal实例
        }
        if (fst == null)
            fst = BigInteger.valueOf(xs);//BigInteger的静态工厂方法
        if (snd == null)
            snd = BigInteger.valueOf(ys);//BigInteger的静态工厂方法
        BigInteger sum = fst.add(snd);
        // 返回通过其他构造方法得到的BigDecimal对象
        return (fst.signum == snd.signum) ?
            new BigDecimal(sum, INFLATED, rscale, 0) :
            new BigDecimal(sum, rscale);
    }
    /**
     * 返回其值为 (this + augend) 的 BigDecimal(根据上下文设置进行舍入)。
     * 如果任一数字为零,并且精度设置为非零,则其他数字(必要时进行舍入)可以作为结果。 
     * @param augend    将添加到此 BigDecimal 中的值
     * @param mc    要使用的上下文
     * @return    this + augend,必要时进行舍入
     */
    public BigDecimal add(BigDecimal augend, MathContext mc) {
        if (mc.precision == 0)
            return add(augend);
        BigDecimal lhs = this;
 
        // 如果值紧凑,可以优化
        // 如果当前对象的intVal为空,分配适当的BigInteger给intVal字段,
        this.inflate();
        // 如果augend的intVal为空,分配适当的BigInteger给intVal字段,
        augend.inflate();
        // 如果任意一个数字都是0,则使用另一个数字(如果需要的话)进行四舍五入和缩放。
        {
            boolean lhsIsZero = lhs.signum() == 0;// 判断lhs 的值是否为0
            boolean augendIsZero = augend.signum() == 0;// 判断augend的值是否为0
 
            if (lhsIsZero || augendIsZero) {
                // 获取lhs.scale()和augend.scale()中较大的一个
                int preferredScale = Math.max(lhs.scale(), augend.scale());
                BigDecimal result;
 
                if (lhsIsZero && augendIsZero) // 都为0
                    return new BigDecimal(BigInteger.ZERO, 0, preferredScale, 0);
                // 判断当前对象的值是否为0,根据mc设置返回一个augend或者lhs四舍五入的BigDecimal对象
                result = lhsIsZero ? doRound(augend, mc) : doRound(lhs, mc);
 
                if (result.scale() == preferredScale) // 判断结果标度是否为最大
                    return result;
                else if (result.scale() > preferredScale) {// 结果标度大于最大
                    BigDecimal scaledResult =
                        new BigDecimal(result.intVal, result.intCompact,
                                       result.scale, 0);
                    // 从当前BigDecimal对象中删除不重要的后置零,直到不能删除更多的零为止。
                    scaledResult.stripZerosToMatchScale(preferredScale);
                    return scaledResult;
                } else { // result.scale < preferredScale
                    int precisionDiff = mc.precision - result.precision();
                    int scaleDiff     = preferredScale - result.scale();
 
                    if (precisionDiff >= scaleDiff)// 精度差大于等于标度差
                        return result.setScale(preferredScale); // 可以达到目标规模
                    else
                        return result.setScale(result.scale() + precisionDiff);
                }
            }
        }
 
        long padding = (long)lhs.scale - augend.scale;
        if (padding != 0) {        // 标度不同; 需要对齐
            // 返回一个长度为2的数组,其条目的和等于lhs和augend参数的整数和。
            BigDecimal arg[] = preAlign(lhs, augend, padding, mc);
            // 匹配arg[0]和arg[1]的scales,以校准它们的最小有效数字
            matchScale(arg);
            lhs    = arg[0];
            augend = arg[1];
        }
 
        BigDecimal d = new BigDecimal(lhs.inflate().add(augend.inflate()),
                                      lhs.scale);
        // 根据mc设置返回一个d的四舍五入的BigDecimal对象
        return doRound(d, mc);
    }
 
    /**
     * 返回一个长度为2的数组,其条目的和等于lhs和augend参数的整数和。
     * 
     * 如果数字的位置参数有足够的差距,值较小的可以浓缩成一个“粘滞位”,如果最终结果的精度不包括小数量级操作数的高阶数,则最终结果将以相同的方式进行四舍五入。
     * 
     * 注意,虽然严格来说这是一种优化,但它的实际应用范围更广。
     * 
     * 这对应于固定精度浮点加法器中的预移位操作;这个方法由于MathContext所决定的结果的可变精度而变得复杂。
     * 更细致的操作可以实现较小幅度操作数上的“右偏移”,从而即使部分重叠的有效数也可以减少较小操作数的位数。
     * @param lhs    当前 BigDecimal 中的值
     * @param augend    将添加到此 BigDecimal 中的值
     * @param padding lhs.scale- padding.scale
     * @param mc    要使用的上下文
     * @return
     */
    private BigDecimal[] preAlign(BigDecimal lhs, BigDecimal augend,
                                  long padding, MathContext mc) {
        assert padding != 0;
        BigDecimal big;
        BigDecimal small;
 
        if (padding < 0) {     // lhs is big;   augend is small
            big   = lhs;
            small = augend;
        } else {               // lhs is small; augend is big
            big   = augend;
            small = lhs;
        }
        /*
         * 这是结果的ulp的估计尺度;它假设结果没有一个真正的add(例如999 + 1 = > - 1000)或任何一个借位的减法取消(例如,100 - 1.2 = > 98.8)
         */
        long estResultUlpScale = (long)big.scale - big.precision() + mc.precision;
 
        /*
         * big的低位是big.scale()。这是真的,不管大的规模是正的还是负的。
         * 小的高阶位是small.scale - (small.precision() - 1)。
         */
        long smallHighDigitPos = (long)small.scale - small.precision() + 1;
        if (smallHighDigitPos > big.scale + 2 &&         // 大小不相交
            smallHighDigitPos > estResultUlpScale + 2) { // small 不可见
            small = BigDecimal
            small = BigDecimal.valueOf(small.signum(),
                                       this.checkScale(Math.max(big.scale, estResultUlpScale) + 3));
        }
 
        // 因为加法是对称的,所以在返回的操作数中保持输入顺序并不重要
        BigDecimal[] result = {big, small};
        return result;
    }
 
    /**
     * 返回一个 BigDecimal,其值为 (this - subtrahend),其标度为 max(this.scale(), subtrahend.scale())。 
     * @param subtrahend    从此 BigDecimal 减去的值
     * @return    this - subtrahend
     */
    public BigDecimal subtract(BigDecimal subtrahend) {
        // 获取当前对象,其值为 (-subtrahend),其标度为 subtrahend.scale()。 
        return add(subtrahend.negate());
    }
 
    /**
     * 返回其值为 (this - subt

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值