最近看到一个好的方法:开方。
public static BigDecimal getBigDecimal(BigDecimal number, int n, int scale, int roundingMode) {
//是否去相反数的标志
boolean negate = false;
//不能开负数次方
if (n < 0) {
throw new ArithmeticException();
}
//负数不能开平方
if (number.compareTo(BigDecimal.ZERO) < 0) {
if (n % 2 == 0) {
throw new ArithmeticException();
} else {
//负数可以开奇数次方,这里取反,后再取回来
number = number.negate();
//取了相反数的标志
negate = true;
}
}
BigDecimal root;
if (n == 0) {
//任何数的0次方为1
root = BigDecimal.ONE;
} else if (n == 1) {
//任何数开1次方是这个数本身
root = number;
} else {
final BigInteger N = BigInteger.valueOf(n);
final BigInteger N2 = BigInteger.TEN.pow(n);
final BigInteger N3 = BigInteger.TEN.pow(n - 1);
final BigInteger NINE = BigInteger.valueOf(9);
BigInteger[] C = new BigInteger[n + 1];
for (int i = 0; i <= n; i++) {
C[i] = combination(n, i);
}
BigInteger integer = number.toBigInteger();
String strInt = integer.toString();
int lenInt = strInt.length();
for (int i = lenInt % n; i < n && i > 0; i++) {
strInt = "0" + strInt;
}
lenInt = (lenInt + n - 1) / n * n;
BigDecimal fraction = number.subtract(number.setScale(0, BigDecimal.ROUND_DOWN));
int lenFrac = (fraction.scale() + n - 1) / n * n;
fraction = fraction.movePointRight(lenFrac);
String strFrac = fraction.toPlainString();
for (int i = strFrac.length(); i < lenFrac; i++) {
strFrac = "0" + strFrac;
}
BigInteger res = BigInteger.ZERO;
BigInteger rem = BigInteger.ZERO;
for (int i = 0; i < lenInt / n; i++) {
rem = rem.multiply(N2);
BigInteger temp = new BigInteger(strInt.substring(i * n, i * n + n));
rem = rem.add(temp);
BigInteger j;
if (res.compareTo(BigInteger.ZERO) != 0) {
j = rem.divide(res.pow(n - 1).multiply(N).multiply(N3));
} else {
j = NINE;
}
BigInteger test = BigInteger.ZERO;
temp = res.multiply(BigInteger.TEN);
while (j.compareTo(BigInteger.ZERO) >= 0) {
test = BigInteger.ZERO;
if (j.compareTo(BigInteger.ZERO) > 0)
for (int k = 1; k <= n; k++)
test = test.add(j.pow(k).multiply(C[k]).multiply(temp.pow(n - k)));
if (test.compareTo(rem) <= 0)
break;
j = j.subtract(BigInteger.ONE);
}
rem = rem.subtract(test);
res = res.multiply(BigInteger.TEN);
res = res.add(j);
}
for (int i = 0; i <= scale; i++) {
rem = rem.multiply(N2);
if (i < lenFrac / n) {
BigInteger temp = new BigInteger(strFrac.substring(i * n, i * n + n));
rem = rem.add(temp);
}
BigInteger j;
if (res.compareTo(BigInteger.ZERO) != 0) {
j = rem.divide(res.pow(n - 1).multiply(N).multiply(N3));
} else {
j = NINE;
}
BigInteger test = BigInteger.ZERO;
BigInteger temp = res.multiply(BigInteger.TEN);
while (j.compareTo(BigInteger.ZERO) >= 0) {
test = BigInteger.ZERO;
if (j.compareTo(BigInteger.ZERO) > 0)
for (int k = 1; k <= n; k++)
test = test.add(j.pow(k).multiply(C[k]).multiply(temp.pow(n - k)));
if (test.compareTo(rem) <= 0)
break;
j = j.subtract(BigInteger.ONE);
}
rem = rem.subtract(test);
res = res.multiply(BigInteger.TEN);
res = res.add(j);
}
root = new BigDecimal(res).movePointLeft(scale + 1);
if (negate) {
//如果取了相反数,那么取回来
root = root.negate();
}
}
/**
* newScale: 保留newScale位小数
* roundingMode: 舍去规则(0 <= roundingMode <= 7)
* ROUND_UP(0):第newScale位小数进1,后面舍去、ROUND_DOWN(1):第newScale位后面的,直接舍去
* ROUND_CEILING(2):如果是正数,同ROUND_UP,如果是负数,同ROUND_DOWN、ROUND_FLOOR(3):与ROUND_CEILING相反
* ROUND_HALF_UP(4):四舍五入:小学学过的那种、
* */
return root.setScale(scale, roundingMode);
}
public static BigInteger combination(int n, int k) {
if (k > n || n < 0 || k < 0) {
return BigInteger.ZERO;
}
if (k > n / 2) {
return combination(n, n - k);
}
BigInteger N1 = BigInteger.ONE;
BigInteger N2 = BigInteger.ONE;
BigInteger N = BigInteger.valueOf(n);
BigInteger K = BigInteger.valueOf(k);
for (int i = 0; i < k; i++) {
N1 = N1.multiply(N);
N2 = N2.multiply(K);
N = N.subtract(BigInteger.ONE);
K = K.subtract(BigInteger.ONE);
}
return N1.divide(N2);
}
注意:第一个为传入参数,即要开方的数字,第二位是开几次方,第三个为保留几位小数,第四位为,舍弃规则,ROUND_HALF_UP为四舍五入