How to do a fractional power on BigDecimal in Java

2020-01-24 02:59发布

In my little project I need to do something like Math.pow(7777.66, 5555.44) only with VERY big numbers. I came across a few solutions:

  • Use double - but the numbers are too big
  • Use BigDecimal.pow but no support for fractional
  • Use the X^(A+B)=X^A*X^B formula (B is the remainder of the second num), but again no support for big X or big A because I still convert to double
  • Use some kind of Taylor series algorithm or something like that - I'm not very good at math so this one is my last option if I don't find any solutions (some libraries or a formula for (A+B)^(C+D)).

Anyone knows of a library or an easy solution? I figured that many people deal with the same problem...

p.s. I found some library called ApFloat that claims to do it approximately, but the results I got were so approximate that even 8^2 gave me 60...

3条回答
女痞
2楼-- · 2020-01-24 03:42

Exponents = logarithms.

Take a look at Logarithm of a BigDecimal

查看更多
迷人小祖宗
3楼-- · 2020-01-24 03:42

The big-math library released under MIT license has a simple static helper BigDecimalMath.log(BigDecimal, MathContext) for log and many other functions not included with BigDecimal. Very simple to use and has lots of benchmarking data to compare performance.

查看更多
叼着烟拽天下
4楼-- · 2020-01-24 03:49

The solution for arguments under 1.7976931348623157E308 (Double.MAX_VALUE) but supporting results with MILLIONS of digits:

Since double supports numbers up to MAX_VALUE (for example, 100! in double looks like this: 9.332621544394415E157), there is no problem to use BigDecimal.doubleValue(). But you shouldn't just do Math.pow(double, double) because if the result is bigger than MAX_VALUE you will just get infinity. SO: use the formula X^(A+B)=X^A*X^B to separate the calculation to TWO powers, the big, using BigDecimal.pow, and the small (remainder of the 2nd argument), using Math.pow, then multiply. X will be copied to DOUBLE - make sure it's not bigger than MAX_VALUE, A will be INT (maximum 2147483647 but the BigDecimal.pow doesn't support integers more than a billion anyway), and B will be double, always less than 1. This way you can do the following (ignore my private constants etc):

    int signOf2 = n2.signum();
    try {
        // Perform X^(A+B)=X^A*X^B (B = remainder)
        double dn1 = n1.doubleValue();
        // Compare the same row of digits according to context
        if (!CalculatorUtils.isEqual(n1, dn1))
            throw new Exception(); // Cannot convert n1 to double
        n2 = n2.multiply(new BigDecimal(signOf2)); // n2 is now positive
        BigDecimal remainderOf2 = n2.remainder(BigDecimal.ONE);
        BigDecimal n2IntPart = n2.subtract(remainderOf2);
        // Calculate big part of the power using context -
        // bigger range and performance but lower accuracy
        BigDecimal intPow = n1.pow(n2IntPart.intValueExact(),
                CalculatorConstants.DEFAULT_CONTEXT);
        BigDecimal doublePow =
            new BigDecimal(Math.pow(dn1, remainderOf2.doubleValue()));
        result = intPow.multiply(doublePow);
    } catch (Exception e) {
        if (e instanceof CalculatorException)
            throw (CalculatorException) e;
        throw new CalculatorException(
            CalculatorConstants.Errors.UNSUPPORTED_NUMBER_ +
                "power!");
    }
    // Fix negative power
    if (signOf2 == -1)
        result = BigDecimal.ONE.divide(result, CalculatorConstants.BIG_SCALE,
                RoundingMode.HALF_UP);

Results examples:

50!^10! = 12.50911317862076252364259*10^233996181

50!^0.06 = 7395.788659356498101260513
查看更多
登录 后发表回答