在一次生产数据运算中
BigDecimal dbValue=new BigDecimal("200.2123333");
此时对数据进行字符串转化操作
dbValue.setScale(2).toString();出现java.lang.ArithmeticException: Rounding necessary异常,出现此问题的原因,具体没有分析出来,查看了jdk的源代码,第一个方法调用的是:
public BigDecimal setScale(int newScale, int roundingMode) {
if (roundingMode < ROUND_UP || roundingMode > ROUND_UNNECESSARY)
throw new IllegalArgumentException("Invalid rounding mode");
int oldScale = this.scale;
if (newScale == oldScale) // easy case
return this;
if (this.signum() == 0) // zero can have any scale
return BigDecimal.valueOf(0, newScale);
long rs = this.intCompact;
if (newScale > oldScale) {//在此处判断新的值是否比老的大,新的值默认为0
int raise = checkScale((long)newScale - oldScale);
BigInteger rb = null;
if (rs == INFLATED ||
(rs = longMultiplyPowerTen(rs, raise)) == INFLATED)
rb = bigMultiplyPowerTen(raise);
return new BigDecimal(rb, rs, newScale,
(precision > 0) ? precision + raise : 0);
} else {//走的else 调用divideAndRound
// newScale < oldScale -- drop some digits
// Can't predict the precision due to the effect of rounding.
int drop = checkScale((long)oldScale - newScale);
if (drop < LONG_TEN_POWERS_TABLE.length)
return divideAndRound(rs, this.intVal,
LONG_TEN_POWERS_TABLE[drop], null,
newScale, roundingMode, newScale);
else
return divideAndRound(rs, this.intVal,
INFLATED, bigTenToThe(drop),
newScale, roundingMode, newScale);
}
}
private static BigDecimal divideAndRound(long ldividend, BigInteger bdividend,
long ldivisor, BigInteger bdivisor,
int scale, int roundingMode,
int preferredScale) {
boolean isRemainderZero; // record remainder is zero or not
int qsign; // quotient sign
long q = 0, r = 0; // store quotient & remainder in long
MutableBigInteger mq = null; // store quotient
MutableBigInteger mr = null; // store remainder
MutableBigInteger mdivisor = null;
boolean isLongDivision = (ldividend != INFLATED && ldivisor != INFLATED);
if (isLongDivision) {
q = ldividend / ldivisor;
if (roundingMode == ROUND_DOWN && scale == preferredScale)
return new BigDecimal(null, q, scale, 0);
r = ldividend % ldivisor;
isRemainderZero = (r == 0);
qsign = ((ldividend < 0) == (ldivisor < 0)) ? 1 : -1;
} else {
if (bdividend == null)
bdividend = BigInteger.valueOf(ldividend);
// Descend into mutables for faster remainder checks
MutableBigInteger mdividend = new MutableBigInteger(bdividend.mag);
mq = new MutableBigInteger();
if (ldivisor != INFLATED) {
r = mdividend.divide(ldivisor, mq);
isRemainderZero = (r == 0);
qsign = (ldivisor < 0) ? -bdividend.signum : bdividend.signum;
} else {
mdivisor = new MutableBigInteger(bdivisor.mag);
mr = mdividend.divide(mdivisor, mq);
isRemainderZero = mr.isZero();
qsign = (bdividend.signum != bdivisor.signum) ? -1 : 1;
}
}
boolean increment = false;
if (!isRemainderZero) {
int cmpFracHalf;
/* Round as appropriate */
if (roundingMode == ROUND_UNNECESSARY) { // 在此出判断 如果==默认值 则抛出异常
throw new ArithmeticException("Rounding necessary");
} else if (roundingMode == ROUND_UP) { // Away from zero
increment = true;
} else if (roundingMode == ROUND_DOWN) { // Towards zero
increment = false;
} else if (roundingMode == ROUND_CEILING) { // Towards +infinity
increment = (qsign > 0);
} else if (roundingMode == ROUND_FLOOR) { // Towards -infinity
increment = (qsign < 0);
} else {
if (isLongDivision || ldivisor != INFLATED) {
if (r <= HALF_LONG_MIN_VALUE || r > HALF_LONG_MAX_VALUE) {
cmpFracHalf = 1; // 2 * r can't fit into long
} else {
cmpFracHalf = longCompareMagnitude(2 * r, ldivisor);
}
} else {
cmpFracHalf = mr.compareHalf(mdivisor);
}
if (cmpFracHalf < 0)
increment = false; // We're closer to higher digit
else if (cmpFracHalf > 0) // We're closer to lower digit
increment = true;
else if (roundingMode == ROUND_HALF_UP)
increment = true;
else if (roundingMode == ROUND_HALF_DOWN)
increment = false;
else // roundingMode == ROUND_HALF_EVEN, true iff quotient is odd
increment = isLongDivision ? (q & 1L) != 0L : mq.isOdd();
}
}
BigDecimal res;
if (isLongDivision)
res = new BigDecimal(null, (increment ? q + qsign : q), scale, 0);
else {
if (increment)
mq.add(MutableBigInteger.ONE);
res = mq.toBigDecimal(qsign, scale);
}
if (isRemainderZero && preferredScale != scale)
res.stripZerosToMatchScale(preferredScale);
return res;
}
此时就想不通当初设计此方法的必要了
但是改变此写法就对了:
dbValue = dbValue.setScale(2, BigDecimal.ROUND_HALF_UP);
;再执行转化:dbValue.toString();