我有一个BigInteger
号,例如超过2 64 。
现在我想计算BigInteger
个数的对数,但方法BigInteger.log()
不存在。如何计算我的大BigInteger
值的(自然)对数?
答案 0 :(得分:34)
如果你想支持任意大整数,那么只做
是不安全的Math.log(bigInteger.doubleValue());
因为如果参数超过double
范围(大约2 ^ 1024或10 ^ 308,即超过300个十进制数字),这将失败。
这是我自己的类,它提供了方法
double logBigInteger(BigInteger val);
double logBigDecimal(BigDecimal val);
BigDecimal expBig(double exponent);
BigDecimal powBig(double a, double b);
即使BigDecimal / BigInteger太大(或太小)无法表示为double
类型,它们也能安全地工作。
/**
* Provides some mathematical operations on BigDecimal and BigInteger
*/
public class BigMath {
protected static final double LOG2 = Math.log(2.0);
protected static final double LOG10 = Math.log(10.0);
// numbers greater than 10^MAX_DIGITS_10 or e^MAX_DIGITS_EXP are considered unsafe ('too big') for floating point operations
protected static final int MAX_DIGITS_EXP = 677;
protected static final int MAX_DIGITS_10 = 294; // ~ MAX_DIGITS_EXP/LN(10)
protected static final int MAX_DIGITS_2 = 977; // ~ MAX_DIGITS_EXP/LN(2)
/**
* Computes the natural logarithm of a BigInteger.
*
* Works for really big integers (practically unlimited), even when the argument
* falls outside the <tt>double</tt> range
*
* Returns Nan if argument is negative, NEGATIVE_INFINITY if zero.
*
* @param val Argument
* @return Natural logarithm, as in <tt>Math.log()</tt>
*/
public static double logBigInteger(BigInteger val) {
if (val.signum() < 1)
return val.signum() < 0 ? Double.NaN : Double.NEGATIVE_INFINITY;
int blex = val.bitLength() - MAX_DIGITS_2; // any value in 60..1023 works ok here
if (blex > 0)
val = val.shiftRight(blex);
double res = Math.log(val.doubleValue());
return blex > 0 ? res + blex * LOG2 : res;
}
/**
* Computes the natural logarithm of a BigDecimal.
*
* Works for really big (or really small) arguments, even outside the double range.
*
* Returns Nan if argument is negative, NEGATIVE_INFINITY if zero.
*
* @param val Argument
* @return Natural logarithm, as in <tt>Math.log()</tt>
*/
public static double logBigDecimal(BigDecimal val) {
if (val.signum() < 1)
return val.signum() < 0 ? Double.NaN : Double.NEGATIVE_INFINITY;
int digits = val.precision() - val.scale();
if (digits < MAX_DIGITS_10 && digits > -MAX_DIGITS_10)
return Math.log(val.doubleValue());
else
return logBigInteger(val.unscaledValue()) - val.scale() * LOG10;
}
/**
* Computes the exponential function, returning a BigDecimal (precision ~ 16).
*
* Works for very big and very small exponents, even when the result
* falls outside the double range
*
* @param exponent Any finite value (infinite or Nan throws IllegalArgumentException)
* @return The value of e (base of the natural logarithms) raised to the given exponent, as in <tt>Math.exp()</tt>
*/
public static BigDecimal expBig(double exponent) {
if (!Double.isFinite(exponent))
throw new IllegalArgumentException("Infinite not accepted: " + exponent);
// e^b = e^(b2+c) = e^b2 2^t with e^c = 2^t
double bc = MAX_DIGITS_EXP;
if (exponent < bc && exponent > -bc)
return new BigDecimal(Math.exp(exponent), MathContext.DECIMAL64);
boolean neg = false;
if (exponent < 0) {
neg = true;
exponent = -exponent;
}
double b2 = bc;
double c = exponent - bc;
int t = (int) Math.ceil(c / LOG10);
c = t * LOG10;
b2 = exponent - c;
if (neg) {
b2 = -b2;
t = -t;
}
return new BigDecimal(Math.exp(b2), MathContext.DECIMAL64).movePointRight(t);
}
/**
* Same as Math.pow(a,b) but returns a BigDecimal (precision ~ 16).
*
* Works even for outputs that fall outside the <tt>double</tt> range
*
* The only limit is that b * log(a) does not overflow the double range
*
* @param a Base. Should be non-negative
* @param b Exponent. Should be finite (and non-negative if base is zero)
* @return Returns the value of the first argument raised to the power of the second argument.
*/
public static BigDecimal powBig(double a, double b) {
if (!(Double.isFinite(a) && Double.isFinite(b)))
throw new IllegalArgumentException(Double.isFinite(b) ? "base not finite: a=" + a : "exponent not finite: b=" + b);
if (b == 0)
return BigDecimal.ONE;
if (b == 1)
return BigDecimal.valueOf(a);
if (a == 0) {
if (b >= 0)
return BigDecimal.ZERO;
else
throw new IllegalArgumentException("0**negative = infinite");
}
if (a < 0) {
throw new IllegalArgumentException("negative base a=" + a);
}
double x = b * Math.log(a);
if (Math.abs(x) < MAX_DIGITS_EXP)
return BigDecimal.valueOf(Math.pow(a, b));
else
return expBig(x);
}
}
答案 1 :(得分:5)
我从谷歌得到了一些帮助,但显然你不需要直接将日志应用到你的大BigInteger号码,因为它可以通过以下方式分解:
928 = 1000 * 0.928
lg 928 = lg 1000 * lg 0.928 = 3 + lg 0.928
因此,您的问题减少到允许任意增加精度的对数的计算/近似值,也许是math.stackexchange.com?
答案 2 :(得分:2)
您需要它的准确度如何?如果您只需要15位数的精确度,那么
BigInteger bi =
double log = Math.log(bi.doubleValue());
这适用于高达1023位的值。之后,该值将不再适合双倍。
答案 3 :(得分:2)
答案 4 :(得分:0)
如果您可以使用Google Guava,并且只需要base 2或base 10日志,则可以使用Guava的BigIntegerMath
类中的方法。