I have this mathematical expression :
∑(2k+1)/(2k)! , k =0,... ,∞ , it is sum from zero to infinity of all fractions in the form (2k+1)/(2k)!
I want to create a method which when passed an integer "n", it will output me the result with n digits after the decimal point.This expression first 100 digits can be viewed here : https://miniwebtool.com/first-n-digits-of-e/?number=100
Here is my code, what I have attempted
package pi.strategy;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
public class Tester {
public static BigInteger factorial(long k) {
return LongStream.range(2, k + 1).mapToObj(BigInteger::valueOf).reduce(BigInteger.ONE,
(current, factSoFar) -> factSoFar.multiply(current));
}
protected static BigDecimal strategy(int precision) {
return IntStream.range(0, precision).mapToObj(i -> computeMember(i, precision + 2))
.reduce(BigDecimal.ZERO, (current, sumSoFar) -> sumSoFar.add(current));
}
static BigDecimal computeMember(int n, int scale) {
final BigDecimal dominator = new BigDecimal((2 * n) + 1);
final BigDecimal enumenator = new BigDecimal(factorial(2 * n));
// System.out.println("Temp Result:" + dominator.divide(enumenator, scale,
// RoundingMode.HALF_UP));
BigDecimal res = dominator.divide(enumenator, scale, RoundingMode.HALF_UP);
return res;
}
public static void main(String... args) {
System.out.println(strategy(6));
}
}
The problem is that when I add fractions, sometimes they will overflow and create extra digits at the end.
In case of strategy(6) , it outputs extra digits 2.71828179 instead of 2.718281
In the case of strategy(5) it outputs wrong answer 2.7182787 instead of 2.71828. Any ideas where is he problem and how can I limit it correctly to output with precision?
解决方案
The problem with your code is that the result is being rounded after each step, and so rounding errors are being summed.
You should use a Fraction class (such as the one described here: https://stackoverflow.com/a/474612) and extract the decimal representation at the end of the process.
EDIT
I'm not a Java developer, so I don't have a proper dev environment set up on my machine. I've tried to update your code to use the BigFraction class I linked earlier:
package pi.strategy;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.RoundingMode;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
public class Tester {
public static BigInteger factorial(long k) {
return LongStream.range(2, k + 1).mapToObj(BigInteger::valueOf).reduce(BigInteger.ONE,
(current, factSoFar) -> factSoFar.multiply(current));
}
protected static BigFraction strategy(int precision) {
return IntStream.range(0, precision).mapToObj(i -> computeMember(i, precision + 2))
.reduce(BigFraction.ZERO, (current, sumSoFar) -> sumSoFar.add(current));
}
static BigFraction computeMember(int n, int scale) {
final BigDecimal numerator = new BigDecimal((2 * n) + 1);
final BigDecimal denominator = new BigDecimal(factorial(2 * n));
return new BigFraction(numerator, denominator);
}
public static void main(String... args) {
final BigFraction result = strategy(6);
System.out.println(result);
System.out.println(result.toBigDecimal());
}
}
It may be more efficient to refactor this code so that it doesn't use the factorial function or factorials are cached and don't have to be recomputed each time from 1.