使用按位运算来优化Java Math

时间:2017-03-23 14:07:14

标签: java

在我的Java类中,我必须使用Bailey-Borwein-Plouffe公式计算Pi的值,精确到15位小数。在公式中,我需要计算16到n的幂(1到5 000 000的整数);

我正在使用的公式

The Bailey–Borwein–Plouffe formula

这是我的代码来计算:

double value = 0.0;

//Calculates and increments value by using the BBP formula
for(int i = 0; i < iterations; i++) {
    if(i == 0) {
        value += (1 / 1) * (
                 (4.0 / ((8 * i) + 1)) - 
                 (2.0 / ((8 * i) + 4)) - 
                 (1.0 / ((8 * i) + 5)) - 
                 (1.0 / ((8 * i) + 6)) );
    } else {
        value += (1.0 / (2L<<(i<<2L))) * (
                 (4.0 / ((8 * i) + 1)) - 
                 (2.0 / ((8 * i) + 4)) - 
                 (1.0 / ((8 * i) + 5)) - 
                 (1.0 / ((8 * i) + 6)) );
    }
}

问题是,我使用按位操作(左移&lt;&lt;)来优化代码,因为如果我们尽可能快地制作程序,我们会获得奖励分数。出于某种原因,无论我尝试什么,从Pi计算的结果数字太大而无法使用。我能够得到数字1到1.5324955e + 54。在那之后,数字溢出,我得到1或0.我试图得到3.14159等但我得到3.1382357295632852因为这个数据溢出。

任何人都可以帮我吗?或者根本不值得使用按位运算来计算功率?

4 个答案:

答案 0 :(得分:2)

可表示为大于零的双倍的最小值是2 -2048 。对于每个高于(2048/4)的项,该公式将达到零为零,即512.到50,000,000是过去的49,999,488。

答案 1 :(得分:1)

你只需要15位数?你走了:

class Class {
  private static final int ITERATION_COUNT = 15;


  public static void main(final String... args) {
    System.out.println(generatePi());
  }

  private static double generatePi() {
    double pi = 0;
    long sixteenPowK = 1;
    for (int k = 0; k < ITERATION_COUNT; k++) {
      pi += 1.0 / sixteenPowK * kthTerm(k);
      sixteenPowK *= 16;
    }
    return pi;
  }

  private static double kthTerm(final int k) {
    return 4.0 / (8.0 * k + 1) 
      - 2.0 / (8.0 * k + 4) 
      - 1.0 / (8.0 * k + 5) 
      - 1.0 / (8.0 * k + 6);
  }
}

我很想看到微观基准

答案 2 :(得分:1)

免责声明:我远非数值方法方面的专家。

一般来说,为了解决任何问题,我避免预先优化。一旦找到解决方案,我就开始优化,只要它以某种方式需要。

在这种情况下,我已将8 * i乘法内联到factor8变量,我删除了循环内的if并计算了{{1}的初始值最重要的是,我在乘法中积累了i = 0的值。

通过这些更改,我设法仅在(1 / 16) ^ i次迭代中计算PI的值,精确到15位小数。这是代码:

11

输出结果为:

public class Pi {

    public static void main(String[] args) {

        int iterations = 11;
        int start = 1;

        double value = 4.0 - 2.0 / 4.0 - 1.0 / 5.0 - 1.0 / 6.0;

        double oneSixteenth = 1.0 / 16.0;
        double oneSixteenthToN = oneSixteenth;

        for (int i = start; i < iterations; i++) {
            double factor8 = 8.0 * i;
            value += oneSixteenthToN * (
                    (4.0 / (factor8 + 1)) -
                            (2.0 / (factor8 + 4)) -
                            (1.0 / (factor8 + 5)) -
                            (1.0 / (factor8 + 6)));
            oneSixteenthToN *= oneSixteenth;
        }

        System.out.println("value = " + value); // our calculated value

        System.out.println("   pi = " + Math.PI); // exact value
    }
}

我必须承认,我不知道代码中累积错误的原因,但我几乎可以肯定它与value = 3.141592653589793 pi = 3.141592653589793 项的计算有关。

答案 3 :(得分:0)

我已经解决了我自己的问题。事实证明,我根本不需要按位操作。由于程序创建了50M迭代来计算Pi,我可以通过使用+ =和* =操作来增加预定义变量。这是我的最终代码,它在0.2秒内以50秒的迭代计算Pi。

//Computes Pi by using the BBP formula
public double computePi(int iterations) //<=50 000 000
{
    final double d = 1 / 16.0;
    double a = 16.0;
    double b = -8;

    double pi = 0.0;

    for(int k = 0; k < iterations; k++)
    {
        a *= d;
        b += 8;
        pi += a * (4.0 / (b + 1)
                - 2.0 / (b + 4)
                - 1.0 / (b + 5)
                - 1.0 / (b + 6));

    }

    return pi;
}

感谢您的所有投入,它帮助了我很多,并让我重新思考我解决这个问题的方法。