简体   繁体   English

使用按位运算来优化Java Math

[英]Working with bitwise operations to optimize Java Math

In my Java class, I have to calculate the value of Pi accurate to 15 decimal places using the Bailey–Borwein–Plouffe formula. 在我的Java类中,我必须使用Bailey-Borwein-Plouffe公式计算Pi精确到15位小数的值。 In the formula, I need to calculate the 16 to the power of n (integer from 1 to 50 000 000); 在公式中,我需要计算16到n的幂(整数从1到5 000 000);

There is the formula I am working with 有我正在使用的公式

Bailey-Borwein-Plouffe公式

And here is my code to calculate that: 这是我的代码来计算:

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

The problem is, I am using a bitwise operation (shift left <<) to optimize the code, as we are given bonus marks if we make the program as fast as possible. 问题是,我使用按位操作(左移<<)来优化代码,因为如果我们尽可能快地制作程序,我们会得到奖励分数。 And for some reason, no matter what I try, the resulting number calculated from Pi is simply too large to work with. 出于某种原因,无论我尝试什么,从Pi计算的结果数字太大而无法使用。 I am able to get the numbers 1 to 1.5324955e+54. 我能够得到数字1到1.5324955e + 54。 After that, the numbers overflow and I get 1 or 0. I am trying to get 3.14159 etc but I get 3.1382357295632852 because of this data overflow. 在那之后,数字溢出,我得到1或0.我试图得到3.14159等但我得到3.1382357295632852因为这个数据溢出。

Can anyone help me with this? 谁能帮我这个? Or is it simply not worth using bitwise operations to calculate power? 或者根本不值得使用按位运算来计算功率?

The smallest value that is expressable as a greater-than-zero double is 2 -2048 . 可表示为大于零的双倍的最小值是2 -2048 That formula is going to hit zero as a double for every term above (2048/4), which is 512. Going up to 50,000,000 is 49,999,488 too far. 对于每个高于(2048/4)的项,该公式将达到零作为双倍,即512.过去达到50,000,000是49,999,488。

You only need 15 digits? 你只需要15位数? Here you go: 干得好:

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

I'd be curious to see a micro benchmark 我很想看到微观基准

DISCLAIMER: I'm far from being an expert in numerical methods. 免责声明:我远不是数值方法的专家。

In general, to solve any problem, I avoid preoptimizations. 一般来说,为了解决任何问题,我避免预先优化。 Once I find the solution, I start to optimize, only if it is somehow required. 一旦找到解决方案,我就开始优化,只要它以某种方式需要。

In this case, I've inlined the 8 * i multiplication to a factor8 variable, I've removed the if inside the loop and have calculated the initial value for i = 0 , and most important, I have accumulated the value of (1 / 16) ^ i in a multiplication. 在这种情况下,我将8 * i乘法内联到factor8变量,我已经删除了循环内部的if并计算了i = 0的初始值,最重要的是,我已经累积了值(1 / 16) ^ i在乘法中。

With these changes, I've managed to calculate the value of PI accurate to 15 decimal places in only 11 iterations. 通过这些更改,我设法在仅11次迭代中将PI的值精确计算到15位小数。 Here's the code: 这是代码:

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
    }
}

The output is: 输出是:

value = 3.141592653589793
   pi = 3.141592653589793

I must admit that I'm unaware of the cause of the cumulative error in your code, but I'm almost sure that it has to do with the calculation of the (1 / 16) ^ i term. 我必须承认,我不知道代码中累积错误的原因,但我几乎可以肯定它与计算(1 / 16) ^ i项有关。

I have solved my own question. 我已经解决了我自己的问题。 It turns out I did not need bitwise operations at all. 事实证明,我根本不需要按位操作。 Since the program creates 50M iterations to calculate Pi, I can increment a predefined variable by using += and *= operations. 由于程序创建50M迭代来计算Pi,我可以通过使用+ =和* =操作来增加预定义变量。 Here is my final code, it computes Pi in 50M iterations in under 0.2 seconds. 这是我的最终代码,它在0.2秒内以50M迭代计算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;
}

Thank you for all of your input, it has helped me a lot and made me rethink my approach to this problem. 感谢您的所有意见,它帮助了我很多,让我重新思考我对这个问题的处理方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM