BigDecimal 加法运算源码解析
BigDecimal 实现高精度加法的核心在于将小数转换为整数和小数点位置的组合,然后进行整数运算,最后根据结果和小数点位置重新组合成最终的小数。
22.33 -> (2233, 2)
0.4 -> (4, 1) -> (40, 2)
22.33 + 0.4 = (2233, 2) + (40, 2) = (2273, 2) = 22.73
通过调试查看 BigDecimal 的 add() 方法实现:
public class Test {
public static void main(String[] args) {
BigDecimal num1 = new BigDecimal("22.33");
System.out.println(num1.add(new BigDecimal("0.4")));
}
}
以下是简化后的 BigDecimal 构造函数及 add 方法的部分代码:
class BigDecimal {
private final int scale;
private transient int precision;
private final transient long intValue;
public BigDecimal(String value) {
this(value.toCharArray(), 0, value.length());
}
public BigDecimal(char[] data, int start, int length) {
this(data, start, length, MathContext.UNLIMITED);
}
public BigDecimal(char[] data, int start, int length, MathContext context) {
int prec = 0;
int scl = 0;
long rs = 0;
boolean dotFound = false;
boolean isCompact = (length <= MAX_COMPACT_DIGITS);
if (isCompact) {
for (; length > 0; start++, length--) {
char c = data[start];
if (c == '0') {
if (prec == 0)
prec = 1;
else if (rs != 0) {
rs *= 10;
++prec;
}
if (dotFound)
++scl;
} else if (c >= '1' && c <= '9') {
int digit = c - '0';
if (prec != 1 || rs != 0)
++prec;
rs = rs * 10 + digit;
if (dotFound)
++scl;
} else if (c == '.') {
if (dotFound)
throw new NumberFormatException("Multiple decimal points found.");
dotFound = true;
}
}
}
this.scale = scl;
this.precision = prec;
this.intValue = rs;
}
}
class BigDecimal {
public BigDecimal add(BigDecimal other) {
if (this.intValue != Long.MIN_VALUE && other.intValue != Long.MIN_VALUE) {
return add(this.intValue, this.scale, other.intValue, other.scale);
}
return null;
}
private static BigDecimal add(long xValue, int xScale, long yValue, int yScale) {
long diff = (long) xScale - yScale;
if (diff == 0) {
return add(xValue, yValue, xScale);
} else if (diff < 0) {
int adjust = checkScale(xValue, -diff);
long scaledX = multiplyByTenPower(xValue, adjust);
if (scaledX != Long.MIN_VALUE) {
return add(scaledX, yValue, yScale);
} else {
BigInteger sum = bigMultiplyByTenPower(xValue, adjust).add(BigInteger.valueOf(yValue));
return ((xValue ^ yValue) >= 0) ?
new BigDecimal(sum, INFLATED, yScale, 0) : valueOf(sum, yScale, 0);
}
} else {
int adjust = checkScale(yValue, diff);
long scaledY = multiplyByTenPower(yValue, adjust);
if (scaledY != Long.MIN_VALUE) {
return add(xValue, scaledY, xScale);
} else {
BigInteger sum = bigMultiplyByTenPower(yValue, adjust).add(BigInteger.valueOf(xValue));
return ((xValue ^ yValue) >= 0) ?
new BigDecimal(sum, INFLATED, xScale, 0) : valueOf(sum, xScale, 0);
}
}
}
private static BigDecimal add(long x, long y, int scale) {
long result = x + y;
if (result != INFLATED)
return BigDecimal.valueOf(result, scale);
return new BigDecimal(BigInteger.valueOf(x).add(BigInteger.valueOf(y)), scale);
}
}