BigInteger的对数

我有一个BigInteger号码,例如超过2 64 。 现在我想计算该BigInteger数的对数,但BigInteger.log()方法不存在。 如何计算我的大BigInteger值的(自然)对数?

如果你想支持任意大整数,那就不安全了

 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 double range * * Returns Nan if argument is negative, NEGATIVE_INFINITY if zero. * * @param val Argument * @return Natural logarithm, as in Math.log() */ 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 Math.log() */ 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 Math.exp() */ 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 double 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); } } 

我得到了谷歌的一些帮助,但显然你不需要直接将日志应用到你的大BigInteger数字,因为它可以通过以下方式分解:

 928 = 1000 * 0.928 lg 928 = lg 1000 * lg 0.928 = 3 + lg 0.928 

因此,您的问题减少到允许任意增加精度的对数的计算/近似,也许math.stackexchange.com?

将它转换为BigDecimal谎言:

 new BigDecimal(val); // where val is a BigInteger 

并在其上调用BigDecimalUtils的日志:D

你需要它准确度如何? 如果您只需要15位精度,那么您可以做到

 BigInteger bi = double log = Math.log(bi.doubleValue()); 

这适用于高达1023位的值。 之后,该值将不再适合双倍。

如果您可以使用Google Guava,并且只需要base 2或base 10日志,则可以使用Guava的BigIntegerMath类中的方法。