BigInteger 的对数

发布于 2024-11-26 11:55:41 字数 150 浏览 1 评论 0原文

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

I have a BigInteger number, for example beyond 264.
Now i want to calculate the logarithm of that BigInteger number, but the method BigInteger.log() does not exist. How do I calculate the (natural) logarithm of my large BigInteger value?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

笑饮青盏花 2024-12-03 11:55:42

如果你想支持任意大的整数,那么这样做是不安全的,

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 类型,它们也可以安全地工作。

import java.math.*;

/**
 * Provides some mathematical operations on {@code BigDecimal} and {@code BigInteger}.
 * Static methods.
 */
public class BigMath {

    public static final double LOG_2 = Math.log(2.0);
    public static final double LOG_10 = Math.log(10.0);

    // numbers greater than 10^MAX_DIGITS_10 or e^MAX_DIGITS_E are considered unsafe ('too big') for floating point operations
    private static final int MAX_DIGITS_10 = 294;
    private static final int MAX_DIGITS_2 = 977; // ~ MAX_DIGITS_10 * LN(10)/LN(2)
    private static final int MAX_DIGITS_E = 677; // ~ MAX_DIGITS_10 * LN(10)

    /**
     * Computes the natural logarithm of a {@link BigInteger} 
     * <p>
     * Works for really big integers (practically unlimited), even when the argument 
     * falls outside the {@code double} range
     * <p>
     * 
     * 
     * @param val Argument
     * @return Natural logarithm, as in {@link java.lang.Math#log(double)}<br>
     * {@code Nan} if argument is negative, {@code NEGATIVE_INFINITY} if zero.
     */
    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 here
        if (blex > 0)
            val = val.shiftRight(blex);
        double res = Math.log(val.doubleValue());
        return blex > 0 ? res + blex * LOG_2 : res;
    }

    /**
     * Computes the natural logarithm of a {@link BigDecimal} 
     * <p>
     * Works for really big (or really small) arguments, even outside the double range.
     * 
     * @param val Argument
     * @return Natural logarithm, as in {@link java.lang.Math#log(double)}<br>
     * {@code Nan} if argument is negative, {@code NEGATIVE_INFINITY} if zero.
     */
    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() * LOG_10;
    }

    /**
     * Computes the exponential function, returning a {@link BigDecimal} (precision ~ 16).
     * <p>
     * Works for very big and very small exponents, even when the result 
     * falls outside the double range.
     *
     * @param exponent Any finite value (infinite or {@code Nan} throws {@code IllegalArgumentException})    
     * @return The value of {@code e} (base of the natural logarithms) raised to the given exponent, 
     * as in {@link java.lang.Math#exp(double)}
     */
    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_E;
        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 / LOG_10);
        c = t * LOG_10;
        b2 = exponent - c;
        if (neg) {
            b2 = -b2;
            t = -t;
        }
        return new BigDecimal(Math.exp(b2), MathContext.DECIMAL64).movePointRight(t);
    }

    /**
     * Same as {@link java.lang.Math#pow(double,double)} but returns a {@link BigDecimal} (precision ~ 16).
     * <p>
     * Works even for outputs that fall outside the {@code double} range.
     * <br>
     * The only limitation is that {@code b * log(a)} cannot exceed the {@code 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;
        else if (b == 1)
            return BigDecimal.valueOf(a);
        if (a <= 0) {
            if (a == 0) {
                if (b >= 0)
                    return BigDecimal.ZERO;
                else
                    throw new IllegalArgumentException("0**negative = infinite b=" + b);
            } else
                throw new IllegalArgumentException("negative base a=" + a);
        }
        double x = b * Math.log(a);
        if (Math.abs(x) < MAX_DIGITS_E)
            return BigDecimal.valueOf(Math.pow(a, b));
        else
            return expBig(x);
    }

}

If you want to support arbitrarily big integers, it's not safe to just do

Math.log(bigInteger.doubleValue());

because this would fail if the argument exceeds the double range (about 2^1024 or 10^308, i.e. more than 300 decimal digits ).

Here's my own class that provides the methods

double logBigInteger(BigInteger val);
double logBigDecimal(BigDecimal val);
BigDecimal expBig(double exponent);
BigDecimal powBig(double a, double b);

They work safely even when the BigDecimal/BigInteger are too big (or too small) to be representable as a double type.

import java.math.*;

/**
 * Provides some mathematical operations on {@code BigDecimal} and {@code BigInteger}.
 * Static methods.
 */
public class BigMath {

    public static final double LOG_2 = Math.log(2.0);
    public static final double LOG_10 = Math.log(10.0);

    // numbers greater than 10^MAX_DIGITS_10 or e^MAX_DIGITS_E are considered unsafe ('too big') for floating point operations
    private static final int MAX_DIGITS_10 = 294;
    private static final int MAX_DIGITS_2 = 977; // ~ MAX_DIGITS_10 * LN(10)/LN(2)
    private static final int MAX_DIGITS_E = 677; // ~ MAX_DIGITS_10 * LN(10)

    /**
     * Computes the natural logarithm of a {@link BigInteger} 
     * <p>
     * Works for really big integers (practically unlimited), even when the argument 
     * falls outside the {@code double} range
     * <p>
     * 
     * 
     * @param val Argument
     * @return Natural logarithm, as in {@link java.lang.Math#log(double)}<br>
     * {@code Nan} if argument is negative, {@code NEGATIVE_INFINITY} if zero.
     */
    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 here
        if (blex > 0)
            val = val.shiftRight(blex);
        double res = Math.log(val.doubleValue());
        return blex > 0 ? res + blex * LOG_2 : res;
    }

    /**
     * Computes the natural logarithm of a {@link BigDecimal} 
     * <p>
     * Works for really big (or really small) arguments, even outside the double range.
     * 
     * @param val Argument
     * @return Natural logarithm, as in {@link java.lang.Math#log(double)}<br>
     * {@code Nan} if argument is negative, {@code NEGATIVE_INFINITY} if zero.
     */
    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() * LOG_10;
    }

    /**
     * Computes the exponential function, returning a {@link BigDecimal} (precision ~ 16).
     * <p>
     * Works for very big and very small exponents, even when the result 
     * falls outside the double range.
     *
     * @param exponent Any finite value (infinite or {@code Nan} throws {@code IllegalArgumentException})    
     * @return The value of {@code e} (base of the natural logarithms) raised to the given exponent, 
     * as in {@link java.lang.Math#exp(double)}
     */
    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_E;
        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 / LOG_10);
        c = t * LOG_10;
        b2 = exponent - c;
        if (neg) {
            b2 = -b2;
            t = -t;
        }
        return new BigDecimal(Math.exp(b2), MathContext.DECIMAL64).movePointRight(t);
    }

    /**
     * Same as {@link java.lang.Math#pow(double,double)} but returns a {@link BigDecimal} (precision ~ 16).
     * <p>
     * Works even for outputs that fall outside the {@code double} range.
     * <br>
     * The only limitation is that {@code b * log(a)} cannot exceed the {@code 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;
        else if (b == 1)
            return BigDecimal.valueOf(a);
        if (a <= 0) {
            if (a == 0) {
                if (b >= 0)
                    return BigDecimal.ZERO;
                else
                    throw new IllegalArgumentException("0**negative = infinite b=" + b);
            } else
                throw new IllegalArgumentException("negative base a=" + a);
        }
        double x = b * Math.log(a);
        if (Math.abs(x) < MAX_DIGITS_E)
            return BigDecimal.valueOf(Math.pow(a, b));
        else
            return expBig(x);
    }

}
甜心小果奶 2024-12-03 11:55:42

我从谷歌那里得到了一些帮助,但显然你不需要直接将 log 应用到你非常大的 BigInteger 数字,因为它可以按以下方式分解:

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

因此,你的问题被简化为允许对数的计算/近似任意增加精度,也许是 math.stackexchange.com?

I had some help from google but apparently you don't need to apply log to your very big BigInteger numbers directly, since it can be broken down in the following way:

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

Your problem is therefore reduced to the computation/approximation of logarithms that allow for arbitrary increasing precision, maybe math.stackexchange.com?

笑咖 2024-12-03 11:55:42

将其转换为 BigDecimal 如下所示:

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

并从 BigDecimalUtils 就可以了:D

Convert it into a BigDecimal liek this:

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

and call log from BigDecimalUtils on it :D

少年亿悲伤 2024-12-03 11:55:42

您需要它有多准确?如果您只需要 15 位精度,则可以执行

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

此操作,这适用于高达 1023 位的值。之后该值将不再适合双精度。

How accurate do you need it to be? If you only need 15 digits of accuracy you can do

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

This would work for values up to 1023 bits. After that the value would not fit into a double anymore.

柏拉图鍀咏恒 2024-12-03 11:55:42

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

如果您需要不同的基数,您始终可以使用对数基数变换公式将其中之一转换为您需要的基数。

If you can use Google Guava, and only require base 2 or base 10 log, you can use methods from Guava's BigIntegerMath class.

If you need a different base, you can always use the logarithm change-of-base formula to convert from one of these, to the one you need.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文