BigDecimal的对数

如何计算BigDecimal的对数?有人知道我可以使用的任何算法吗?


到目前为止,我的谷歌搜索工作提出了(无用的)想法,即仅转换为double并使用Math.log。


我将提供所需答案的精确度。


编辑:任何基地都可以。如果在base x中更简单,我会这样做。


拉风的咖菲猫
浏览 343回答 3
3回答

撒科打诨

Java Number Cruncher:《 Java数值计算程序员指南》提供了使用牛顿方法的解决方案。这本书的源代码在这里。以下内容摘自第12.5章大十进制函数(p330&p331):/**&nbsp;* Compute the natural logarithm of x to a given scale, x > 0.&nbsp;*/public static BigDecimal ln(BigDecimal x, int scale){&nbsp; &nbsp; // Check that x > 0.&nbsp; &nbsp; if (x.signum() <= 0) {&nbsp; &nbsp; &nbsp; &nbsp; throw new IllegalArgumentException("x <= 0");&nbsp; &nbsp; }&nbsp; &nbsp; // The number of digits to the left of the decimal point.&nbsp; &nbsp; int magnitude = x.toString().length() - x.scale() - 1;&nbsp; &nbsp; if (magnitude < 3) {&nbsp; &nbsp; &nbsp; &nbsp; return lnNewton(x, scale);&nbsp; &nbsp; }&nbsp; &nbsp; // Compute magnitude*ln(x^(1/magnitude)).&nbsp; &nbsp; else {&nbsp; &nbsp; &nbsp; &nbsp; // x^(1/magnitude)&nbsp; &nbsp; &nbsp; &nbsp; BigDecimal root = intRoot(x, magnitude, scale);&nbsp; &nbsp; &nbsp; &nbsp; // ln(x^(1/magnitude))&nbsp; &nbsp; &nbsp; &nbsp; BigDecimal lnRoot = lnNewton(root, scale);&nbsp; &nbsp; &nbsp; &nbsp; // magnitude*ln(x^(1/magnitude))&nbsp; &nbsp; &nbsp; &nbsp; return BigDecimal.valueOf(magnitude).multiply(lnRoot)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .setScale(scale, BigDecimal.ROUND_HALF_EVEN);&nbsp; &nbsp; }}/**&nbsp;* Compute the natural logarithm of x to a given scale, x > 0.&nbsp;* Use Newton's algorithm.&nbsp;*/private static BigDecimal lnNewton(BigDecimal x, int scale){&nbsp; &nbsp; int&nbsp; &nbsp; &nbsp; &nbsp; sp1 = scale + 1;&nbsp; &nbsp; BigDecimal n&nbsp; &nbsp;= x;&nbsp; &nbsp; BigDecimal term;&nbsp; &nbsp; // Convergence tolerance = 5*(10^-(scale+1))&nbsp; &nbsp; BigDecimal tolerance = BigDecimal.valueOf(5)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .movePointLeft(sp1);&nbsp; &nbsp; // Loop until the approximations converge&nbsp; &nbsp; // (two successive approximations are within the tolerance).&nbsp; &nbsp; do {&nbsp; &nbsp; &nbsp; &nbsp; // e^x&nbsp; &nbsp; &nbsp; &nbsp; BigDecimal eToX = exp(x, sp1);&nbsp; &nbsp; &nbsp; &nbsp; // (e^x - n)/e^x&nbsp; &nbsp; &nbsp; &nbsp; term = eToX.subtract(n)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .divide(eToX, sp1, BigDecimal.ROUND_DOWN);&nbsp; &nbsp; &nbsp; &nbsp; // x - (e^x - n)/e^x&nbsp; &nbsp; &nbsp; &nbsp; x = x.subtract(term);&nbsp; &nbsp; &nbsp; &nbsp; Thread.yield();&nbsp; &nbsp; } while (term.compareTo(tolerance) > 0);&nbsp; &nbsp; return x.setScale(scale, BigDecimal.ROUND_HALF_EVEN);}/**&nbsp;* Compute the integral root of x to a given scale, x >= 0.&nbsp;* Use Newton's algorithm.&nbsp;* @param x the value of x&nbsp;* @param index the integral root value&nbsp;* @param scale the desired scale of the result&nbsp;* @return the result value&nbsp;*/public static BigDecimal intRoot(BigDecimal x, long index,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;int scale){&nbsp; &nbsp; // Check that x >= 0.&nbsp; &nbsp; if (x.signum() < 0) {&nbsp; &nbsp; &nbsp; &nbsp; throw new IllegalArgumentException("x < 0");&nbsp; &nbsp; }&nbsp; &nbsp; int&nbsp; &nbsp; &nbsp; &nbsp; sp1 = scale + 1;&nbsp; &nbsp; BigDecimal n&nbsp; &nbsp;= x;&nbsp; &nbsp; BigDecimal i&nbsp; &nbsp;= BigDecimal.valueOf(index);&nbsp; &nbsp; BigDecimal im1 = BigDecimal.valueOf(index-1);&nbsp; &nbsp; BigDecimal tolerance = BigDecimal.valueOf(5)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .movePointLeft(sp1);&nbsp; &nbsp; BigDecimal xPrev;&nbsp; &nbsp; // The initial approximation is x/index.&nbsp; &nbsp; x = x.divide(i, scale, BigDecimal.ROUND_HALF_EVEN);&nbsp; &nbsp; // Loop until the approximations converge&nbsp; &nbsp; // (two successive approximations are equal after rounding).&nbsp; &nbsp; do {&nbsp; &nbsp; &nbsp; &nbsp; // x^(index-1)&nbsp; &nbsp; &nbsp; &nbsp; BigDecimal xToIm1 = intPower(x, index-1, sp1);&nbsp; &nbsp; &nbsp; &nbsp; // x^index&nbsp; &nbsp; &nbsp; &nbsp; BigDecimal xToI =&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; x.multiply(xToIm1)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .setScale(sp1, BigDecimal.ROUND_HALF_EVEN);&nbsp; &nbsp; &nbsp; &nbsp; // n + (index-1)*(x^index)&nbsp; &nbsp; &nbsp; &nbsp; BigDecimal numerator =&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; n.add(im1.multiply(xToI))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .setScale(sp1, BigDecimal.ROUND_HALF_EVEN);&nbsp; &nbsp; &nbsp; &nbsp; // (index*(x^(index-1))&nbsp; &nbsp; &nbsp; &nbsp; BigDecimal denominator =&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; i.multiply(xToIm1)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .setScale(sp1, BigDecimal.ROUND_HALF_EVEN);&nbsp; &nbsp; &nbsp; &nbsp; // x = (n + (index-1)*(x^index)) / (index*(x^(index-1)))&nbsp; &nbsp; &nbsp; &nbsp; xPrev = x;&nbsp; &nbsp; &nbsp; &nbsp; x = numerator&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .divide(denominator, sp1, BigDecimal.ROUND_DOWN);&nbsp; &nbsp; &nbsp; &nbsp; Thread.yield();&nbsp; &nbsp; } while (x.subtract(xPrev).abs().compareTo(tolerance) > 0);&nbsp; &nbsp; return x;}/**&nbsp;* Compute e^x to a given scale.&nbsp;* Break x into its whole and fraction parts and&nbsp;* compute (e^(1 + fraction/whole))^whole using Taylor's formula.&nbsp;* @param x the value of x&nbsp;* @param scale the desired scale of the result&nbsp;* @return the result value&nbsp;*/public static BigDecimal exp(BigDecimal x, int scale){&nbsp; &nbsp; // e^0 = 1&nbsp; &nbsp; if (x.signum() == 0) {&nbsp; &nbsp; &nbsp; &nbsp; return BigDecimal.valueOf(1);&nbsp; &nbsp; }&nbsp; &nbsp; // If x is negative, return 1/(e^-x).&nbsp; &nbsp; else if (x.signum() == -1) {&nbsp; &nbsp; &nbsp; &nbsp; return BigDecimal.valueOf(1)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .divide(exp(x.negate(), scale), scale,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BigDecimal.ROUND_HALF_EVEN);&nbsp; &nbsp; }&nbsp; &nbsp; // Compute the whole part of x.&nbsp; &nbsp; BigDecimal xWhole = x.setScale(0, BigDecimal.ROUND_DOWN);&nbsp; &nbsp; // If there isn't a whole part, compute and return e^x.&nbsp; &nbsp; if (xWhole.signum() == 0) return expTaylor(x, scale);&nbsp; &nbsp; // Compute the fraction part of x.&nbsp; &nbsp; BigDecimal xFraction = x.subtract(xWhole);&nbsp; &nbsp; // z = 1 + fraction/whole&nbsp; &nbsp; BigDecimal z = BigDecimal.valueOf(1)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .add(xFraction.divide(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; xWhole, scale,&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; BigDecimal.ROUND_HALF_EVEN));&nbsp; &nbsp; // t = e^z&nbsp; &nbsp; BigDecimal t = expTaylor(z, scale);&nbsp; &nbsp; BigDecimal maxLong = BigDecimal.valueOf(Long.MAX_VALUE);&nbsp; &nbsp; BigDecimal result&nbsp; = BigDecimal.valueOf(1);&nbsp; &nbsp; // Compute and return t^whole using intPower().&nbsp; &nbsp; // If whole > Long.MAX_VALUE, then first compute products&nbsp; &nbsp; // of e^Long.MAX_VALUE.&nbsp; &nbsp; while (xWhole.compareTo(maxLong) >= 0) {&nbsp; &nbsp; &nbsp; &nbsp; result = result.multiply(&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; intPower(t, Long.MAX_VALUE, scale))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .setScale(scale, BigDecimal.ROUND_HALF_EVEN);&nbsp; &nbsp; &nbsp; &nbsp; xWhole = xWhole.subtract(maxLong);&nbsp; &nbsp; &nbsp; &nbsp; Thread.yield();&nbsp; &nbsp; }&nbsp; &nbsp; return result.multiply(intPower(t, xWhole.longValue(), scale))&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; .setScale(scale, BigDecimal.ROUND_HALF_EVEN);}

慕田峪9158850

一种关系不好的小算法,适用于大量数字log(AB) = log(A) + log(B)。以下是以10为基数的方法(您可以将其转换为任何其他对数基数):计算答案中的小数位数。那是对数的必不可少的部分,再加上一个。例如:floor(log10(123456)) + 1是6,因为123456有6位数字。如果您需要的只是对数的整数部分,可以在这里停止:只需从步骤1的结果中减去1。要获得对数的小数部分,请将数字除以10^(number of digits),然后使用math.log10()(或其他方法;计算对数的对数)(如果没有其他可用方法,则使用简单的序列近似),然后将其加到整数部分。示例:要获取的小数部分log10(123456),请计算math.log10(0.123456) = -0.908...并将其添加到步骤1的结果中6 + -0.908 = 5.092,即log10(123456)。请注意,您基本上只是在大数前面加上小数点;在您的用例中可能有一种优化此方法的好方法,对于很大的数字,您甚至不必费心去抓住所有数字-&nbsp;log10(0.123)近似于log10(0.123456789)。

HUWWW

这是超级快的,因为:没有 toString()无BigInteger数学(牛顿/续分数)甚至没有实例化一个新的 BigInteger仅使用固定数量的非常快速的操作一次通话大约需要20微秒(每秒大约5万次通话)但:仅适用于 BigInteger解决方法BigDecimal(未测试速度):移动小数点,直到值> 2 ^ 53使用toBigInteger()(div内部使用)该算法利用了可以将对数计算为指数和尾数对数之和的事实。例如:12345有5位数字,因此以10为底的对数介于4和5之间。log(12345)= 4 + log(1.2345)= 4.09149 ...(以10为底的对数)该函数计算以2为底的对数,因为找到占用的位数很简单。public double log(BigInteger val){&nbsp; &nbsp; // Get the minimum number of bits necessary to hold this value.&nbsp; &nbsp; int n = val.bitLength();&nbsp; &nbsp; // Calculate the double-precision fraction of this number; as if the&nbsp; &nbsp; // binary point was left of the most significant '1' bit.&nbsp; &nbsp; // (Get the most significant 53 bits and divide by 2^53)&nbsp; &nbsp; long mask = 1L << 52; // mantissa is 53 bits (including hidden bit)&nbsp; &nbsp; long mantissa = 0;&nbsp; &nbsp; int j = 0;&nbsp; &nbsp; for (int i = 1; i < 54; i++)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; j = n - i;&nbsp; &nbsp; &nbsp; &nbsp; if (j < 0) break;&nbsp; &nbsp; &nbsp; &nbsp; if (val.testBit(j)) mantissa |= mask;&nbsp; &nbsp; &nbsp; &nbsp; mask >>>= 1;&nbsp; &nbsp; }&nbsp; &nbsp; // Round up if next bit is 1.&nbsp; &nbsp; if (j > 0 && val.testBit(j - 1)) mantissa++;&nbsp; &nbsp; double f = mantissa / (double)(1L << 52);&nbsp; &nbsp; // Add the logarithm to the number of bits, and subtract 1 because the&nbsp; &nbsp; // number of bits is always higher than necessary for a number&nbsp; &nbsp; // (ie. log2(val)<n for every val).&nbsp; &nbsp; return (n - 1 + Math.log(f) * 1.44269504088896340735992468100189213742664595415298D);&nbsp; &nbsp; // Magic number converts from base e to base 2 before adding. For other&nbsp; &nbsp; // bases, correct the result, NOT this number!}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Java