为什么浮点数无法精确计算
计算机底层只能用二进制表示数,因此只能精确表示x*2^y类型的数,不能被此形式计算得来的数则只能通过多精度的方式近似地表示出来。
底层数字表示
BigDecimal能更精确表示带小数点的数值,因为采用了long intCompact和int scale来表示数值,
如果位数过大,不能用intCompact表示还会用BigInteger这个对象来表示大数字,本质是一个整形数组,而不是浮点型的科学计数法。
/**
* The unscaled value of this BigDecimal, as returned by {@link
* #unscaledValue}.
*
* @serial
* @see #unscaledValue
*/
private final BigInteger intVal;
/**
* The scale of this BigDecimal, as returned by {@link #scale}.
*
* @serial
* @see #scale
*/
private final int scale; // Note: this may have any value, so
// calculations must be done in longs
/**
* The number of decimal digits in this BigDecimal, or 0 if the
* number of digits are not known (lookaside information). If
* nonzero, the value is guaranteed correct. Use the precision()
* method to obtain and set the value if it might be 0. This
* field is mutable until set nonzero.
*
* @since 1.5
*/
private transient int precision;
/**
* Sentinel value for {@link #intCompact} indicating the
* significand information is only available from {@code intVal}.
*/
static final long INFLATED = Long.MIN_VALUE;
private static final BigInteger INFLATED_BIGINT = BigInteger.valueOf(INFLATED);
/**
* If the absolute value of the significand of this BigDecimal is
* less than or equal to {@code Long.MAX_VALUE}, the value can be
* compactly stored in this field and used in computations.
*/
private final transient long intCompact;
乘法计算
判断结果是否会溢出,不会就直接是long*long,溢出则会调用BigInteger的乘法
private static long multiply(long x, long y){
long product = x * y;
long ax = Math.abs(x);
long ay = Math.abs(y);
if (((ax | ay) >>> 31 == 0) || (y == 0) || (product / y == x)){
return product;
}
return INFLATED;
}
private static BigDecimal multiply(long x, long y, int scale) {
long product = multiply(x, y);
if(product!=INFLATED) {
return valueOf(product,scale);
}
return new BigDecimal(BigInteger.valueOf(x).multiply(y),INFLATED,scale,0);
}
private static BigDecimal multiply(long x, BigInteger y, int scale) {
if(x==0) {
return zeroValueOf(scale);
}
return new BigDecimal(y.multiply(x),INFLATED,scale,0);
}
private static BigDecimal multiply(BigInteger x, BigInteger y, int scale) {
return new BigDecimal(x.multiply(y),INFLATED,scale,0);
}
网友评论