【问题标题】:How to do a fractional power on BigDecimal in Java?如何在 Java 中对 BigDecimal 进行分数幂运算?
【发布时间】:2011-04-04 12:48:01
【问题描述】:

在我的小项目中,我只需要对非常大的数字做类似Math.pow(7777.66, 5555.44) 的操作。我遇到了一些解决方案:

  • 使用double - 但数字太大
  • 使用BigDecimal.pow,但不支持小数
  • 使用 X^(A+B)=X^A*X^B 公式(B 是第二个数字的余数),但同样不支持大 X 或大 A,因为我仍然转换为双精度
  • 使用某种泰勒级数算法或类似的东西 - 我的数学不太好,所以如果我找不到任何解决方案,这是我最后的选择((A+B)^(C+D) 的一些库或公式)。

有人知道图书馆或简单的解决方案吗?我想很多人都在处理同样的问题......

附言 我发现了一个名为 ApFloat 的库,声称可以近似地做到这一点,但我得到的结果是如此近似,以至于即使 8^2 给了我 60...

【问题讨论】:

  • 您能否举个例子说明您尝试完成的工作,8^2 != 64 听起来很糟糕,需要缩小 2^100^100。
  • 我必须说我尝试了公式技巧,到目前为止,即使是数百万位数的数字,它也能正常工作! (看起来我对double和int一无所知)......示例:50!^ 10! = 12.50911317862076252364259*10^233996181 50!^0.06 = 7395.788659356498101260513 在这里发布的代码有点长,但你明白了 X^(A+B)=X^A*X^B... 现在我是试图了解它如何以及为什么(以及是否)真正适用于如此庞大的数字。
  • 我已经给出了解决方案stackoverflow.com/questions/11848887/…

标签: java formula bigdecimal pow


【解决方案1】:

1.7976931348623157E308 (Double.MAX_VALUE) 下参数的解决方案,但支持数百万位数的结果:

由于 double 支持最大 MAX_VALUE 的数字(例如,100!在 double 中看起来像这样:9.332621544394415E157),所以使用 BigDecimal.doubleValue() 没有问题。但是你不应该只做 Math.pow(double, double) 因为如果结果大于 MAX_VALUE 你只会得到无穷大。所以:使用公式 X^(A+B)=X^A*X^B 将计算分成两个幂,大的,使用 BigDecimal.pow,小(第二个参数的余数),使用 Math。 pow,然后相乘。 X 将被复制到 DOUBLE - 确保它不大于 MAX_VALUE,A 将是 INT(最大 2147483647,但 BigDecimal.pow 不支持超过 10 亿的整数),B 将是双倍的,始终小于 1。这样您就可以执行以下操作(忽略我的私有常量等):

    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);

结果示例:

50!^10! = 12.50911317862076252364259*10^233996181

50!^0.06 = 7395.788659356498101260513

【讨论】:

  • 如果没有 CalculatorUtilsCalculatorConstantsCalculatorException 类,这将毫无用处
【解决方案2】:

指数 = 对数。

看看Logarithm of a BigDecimal

【讨论】:

  • 引用这个问题的公认答案的源代码有更多的解决方案,而不仅仅是自然日志。
  • @Prunge - 谢谢。我实际上从来没有说过任何关于自然日志的事情。真的,如果您查看 Gene Marin 接受的答案,他所描述的是对数。 X^(A+B)=X^AX^B 相当于说 log(base X)A + log(base X)B = log(base X)(AB)。这应该允许您将数字带到一个可管理的数量级。
【解决方案3】:

在 MIT 许可下发布的 big-math 库有一个简单的静态助手 BigDecimalMath.log(BigDecimal, MathContext) 用于日志和 BigDecimal 未包含的许多其他功能。使用非常简单,并且有大量基准数据来比较性能。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-30
    • 1970-01-01
    • 1970-01-01
    • 2015-07-20
    • 2018-07-18
    • 1970-01-01
    • 2011-10-29
    • 1970-01-01
    相关资源
    最近更新 更多