Better approximation of e with Java

2019-06-21 21:13发布

问题:

I would like to approximate the value of e to any desired precision. What is the best way to do this? The most I've been able to get is e = 2.7182818284590455. Any examples on a modification of the following code would be appreciated.

public static long fact(int x){
    long prod = 1;
    for(int i = 1; i <= x; i++)
        prod = prod * i;
    return prod;
}//fact

public static void main(String[] args) {
    double e = 1;
    for(int i = 1; i < 50; i++)
        e = e + 1/(double)(fact(i));
    System.out.print("e = " + e);
}//main

回答1:

Use a BigDecimal instead of a double.

BigDecimal e = BigDecimal.ONE;
BigDecimal fact = BigDecimal.ONE;

for(int i=1;i<100;i++) {
  fact = fact.multiply(new BigDecimal(i));

  e = e.add(BigDecimal.ONE.divide(fact, new MathContext(10000, RoundingMode.HALF_UP)));
}


回答2:

Your main problem is that double has very limited precision. If you want arbitrary precision, you'll have to use BigDecimal. The next problem you're going to run into is the limited range of long which you're going to exceed very quickly with the factorial - there you can use BigInteger.



回答3:

Have you taken a look at the arbitrary-precision arithmetic in java.util.BigDecimal?

import java.math.BigDecimal;
import java.math.MathContext;
public class BigExp {
  public static void main(String[] args) {
BigDecimal FIFTY =new BigDecimal("50");
BigDecimal e = BigDecimal.ZERO;
BigDecimal f = BigDecimal.ONE;
MathContext context = new MathContext(1000);

for (BigDecimal i=BigDecimal.ONE; i.compareTo(FIFTY)<0; i=i.add(BigDecimal.ONE)) {
  f = f.multiply(i, context);
  e = e.add(i.divide(f,context),context);

  System.out.println("e = " + e);
}
  }
}


回答4:

You will get better results if you count from 49 to 1 instead of 1 to 49 as now.



回答5:

Went with a variation of Zed and mobrule's code. Works great, thanks! More performance advice anyone?

public static BigDecimal factorial(int x){
    BigDecimal prod = new BigDecimal("1");
    for(int i = x; i > 1; i--)
        prod = prod.multiply(new BigDecimal(i));
    return prod;
}//fact

public static void main(String[] args) {
    MathContext mc = new MathContext(1000);
    BigDecimal e = new BigDecimal("1", mc);
    for(int i = 1; i < 1000; i++)
        e = e.add(BigDecimal.ONE.divide(factorial(i), mc));
    System.out.print("e = " + e);
}//main 


回答6:

More performance advice anyone?

Yes, your calculation of factorial is as inefficient as it gets. It would be better to move that inside the loop where you're summing the terms. The way you're doing things turns a O(N) problem into a O(N^2) problem.

And if this was a real calculation that needed factorials, I'd recommend a table lookup or the incomplete gamma function as the correct way to do it.

The only thing you could have done worse from a performance point of view is a recursive factorial calculation. Then you'd have the additional problem of a huge stack.



回答7:

To understand why you cannot get "any desired precision" with double, read this classic paper:

What Every Computer Scientist Should Know About Floating-Point Arithmetic

Note that that's a quite technical paper. For more basic details of how floating-point numbers work, see this Wikipedia article: Double precision floating-point format