BigDemical add 源码分析

BigDecimal 实现精度运算的本质是,将小数转为去除小数点后的整数 + 小数点所在的位置
再进行相关整数运算,最后根据计算出来的整数跟小数点位置,加上小数点,进行返回

22.33 -> (2233, 2)
0.4   -> (4, 1) -> (40, 2)
22.33 + 0.4 = (2233, 2) + (40, 2) = (2273, 2) = 22.73

debug 查看 BigDecimal add() 的实现过程

// 测试用例
public class Test {

    public static void main(String[] args) {
        BigDecimal g = new BigDecimal("22.33");
        System.out.println(g.add(new BigDecimal("0.4")));
    }
}
/**
* 进行了删减,完整代码请自行查看
* new BigDcimal("22.33") new BigDecimal("0.4")实现过程
*/
class BigDecimal {

    /**
     * 该 BigDecimal 的小数点所在位置
     * new BigDcimal("22.33") -> scale = 2
     * new BigDcimal("0.4") -> scale = 1
     */
    private final int scale;

    /**
     * 该 BigDecimal 的有效值位数(initCompact 的长度)
     * new BigDcimal("22.33") -> precision = 4
     * new BigDcimal("0.4") -> precision = 1
     */
    private transient int precision;

    /**
     * 如果此 BigDecimal 有效位的绝对值小于或等于 Long.MaxValue,
     * 该值可以紧凑地存储在该字段中并用于计算。
     * new BigDcimal("22.33") -> intCompact = 2233
     * new BigDcimal("0.4") -> intCompact = 4
     */ 
    private final transient long intCompact;
    
    /**
	* new BigDecimal("22.33") 构造
	*/
    public BigDecimal(String val) {
        // 将传过来的字符串,转为字符数组
        // 22.33 -> ['2', '2', '.', '3', '3']
        this(val.toCharArray(), 0, val.length());
    }
    
    public BigDecimal(char[] in, int offset, int len) {
        this(in,offset,len,MathContext.UNLIMITED);
    }
    
    /**
     * 构造完成后,赋值全局属性
     */
    public BigDecimal(char[] in, int offset, int len, MathContext mc) {
        int prec = 0;                 // 记录 precision 的值
        int scl = 0;                  // 记录 scale 的值
        long rs = 0;                  // 记录 intCompact 的值
        try {
            boolean dot = false;             // 当为 '.' 的时候转为 true
            boolean isCompact = (len <= MAX_COMPACT_DIGITS);
            
            if (isCompact) {
                // ['2', '2', '.', '3', '3']
                for (; len > 0; offset++, len--) {
                    c = in[offset];
                    if ((c == '0')) {
                        // 判断 0 是否是第一个值 (0.13 还是 1.03)
                        if (prec == 0)
                            prec = 1;
                        else if (rs != 0) {
                            // 数据进行累积
                            rs *= 10;
                            ++prec;
                        } // 检测到小数点后进行,小数点后位数的 累加
                        if (dot)
                            ++scl;
                    } else if ((c >= '1' && c <= '9')) {
                        int digit = c - '0';
                        if (prec != 1 || rs != 0)
                            ++prec; 
                        // 数据进行累积
                        rs = rs * 10 + digit;
                        if (dot)
                            ++scl;
                    } else if (c == '.') {   
                        // 检测到小数点
                        if (dot) // 有两个小数点报错
                            throw new NumberFormatException("Character array"
                                + " contains more than one decimal point.");
                        dot = true;
                    } 
                }
            } 
        } catch (ArrayIndexOutOfBoundsException | NegativeArraySizeException e) {
            NumberFormatException nfe = new NumberFormatException();
            nfe.initCause(e);
            throw nfe;
        }
        // 赋值
        this.scale = scl;
        this.precision = prec;
        this.intCompact = rs;
    }
}
/**
* ("22.33").add("0.4") 实现过程
*/
class BigDecimal {
	public BigDecimal add(BigDecimal augend) {
        if (this.intCompact != Long.MIN_VALUE) {
            if ((augend.intCompact != Long.MIN_VALUE)) {
                // add(2233, 2, 4, 1)
                return add(this.intCompact, this.scale, augend.intCompact, augend.scale);
            }
        }
    }
    
    /**
     * (2233, 2, 4, 1)
     */
    private static BigDecimal add(final long xs, int scale1, final long ys, int scale2) {
        // 小数位后面数据的差值,sdiff = 1
        long sdiff = (long) scale1 - scale2;
        if (sdiff == 0) {
            return add(xs, ys, scale1);
        } else if (sdiff < 0) {
            int raise = checkScale(xs,-sdiff);
            long scaledX = longMultiplyPowerTen(xs, raise);
            if (scaledX != Long.MIN_VALUE) {
                return add(scaledX, ys, scale2);
            } else {
                BigInteger bigsum = bigMultiplyPowerTen(xs,raise).add(ys);
                return ((xs^ys)>=0) ? // same sign test
                    new BigDecimal(bigsum, INFLATED, scale2, 0)
                    : valueOf(bigsum, scale2, 0);
            }
        } else {
            // raise = 1
            int raise = checkScale(ys,sdiff);
            // scaledY = ys * 10^raise = ys * 10 = 40
            long scaledY = longMultiplyPowerTen(ys, raise);
            if (scaledY != Long.MIN_VALUE) {
                // add(2233, 40, 2)
                return add(xs, scaledY, scale1);
            } else {
                BigInteger bigsum = bigMultiplyPowerTen(ys,raise).add(xs);
                return ((xs^ys)>=0) ?
                    new BigDecimal(bigsum, INFLATED, scale1, 0)
                    : valueOf(bigsum, scale1, 0);
            }
        }
    }
    
    /**
     * (2233, 40, 2)
     */
    private static BigDecimal add(long xs, long ys, int scale){
        // 2273
        long sum = add(xs, ys);
        if (sum!=INFLATED)
            // BigDecimal.valueOf(2273, 2) -> new BigDecimal("22.73")
            return BigDecimal.valueOf(sum, scale);
        return new BigDecimal(BigInteger.valueOf(xs).add(ys), scale);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值