public static BigDecimal Zeta(int n, MathContext mc) { if (n <= 0) throw new NotSupportedException("Zeta at negative argument " + n + " not supported"); if (n == 1) throw new ArithmeticException("Pole at zeta(1) "); if (n%2 == 0) { /* Even indices. Abramowitz-Stegun 23.2.16. Start with 2^(n-1)*B(n)/n! */ Rational b = Bernoulli.Default[n].Abs(); b = b.Divide(Factorial.Default[n]); b = b.Multiply(BigInteger.One.ShiftLeft(n - 1)); /* to be multiplied by pi^n. Absolute error in the result of pi^n is n times * error in pi times pi^(n-1). Relative error is n*error(pi)/pi, requested by mc. * Need one more digit in pi if n=10, two digits if n=100 etc, and add one extra digit. */ var mcpi = new MathContext(mc.Precision + (int) (System.Math.Log10(10.0*n))); BigDecimal piton = PiRound(mcpi).Pow(n, mc); return MultiplyRound(piton, b); } if (n == 3) { /* Broadhurst BBP <a href="http://arxiv.org/abs/math/9803067">arXiv:math/9803067</a> * Error propagation: S31 is roughly 0.087, S33 roughly 0.131 */ int[] a31 = {1, -7, -1, 10, -1, -7, 1, 0}; int[] a33 = {1, 1, -1, -2, -1, 1, 1, 0}; BigDecimal S31 = BroadhurstBbp(3, 1, a31, mc); BigDecimal S33 = BroadhurstBbp(3, 3, a33, mc); S31 = S31.Multiply(new BigDecimal(48)); S33 = S33.Multiply(new BigDecimal(32)); return S31.Add(S33).Divide(new BigDecimal(7), mc); } if (n == 5) { /* Broadhurst BBP <a href=http://arxiv.org/abs/math/9803067">arXiv:math/9803067</a> * Error propagation: S51 is roughly -11.15, S53 roughly 22.165, S55 is roughly 0.031 * 9*2048*S51/6265 = -3.28. 7*2038*S53/61651= 5.07. 738*2048*S55/61651= 0.747. * The result is of the order 1.03, so we add 2 digits to S51 and S52 and one digit to S55. */ int[] a51 = {31, -1614, -31, -6212, -31, -1614, 31, 74552}; int[] a53 = {173, 284, -173, -457, -173, 284, 173, -111}; int[] a55 = {1, 0, -1, -1, -1, 0, 1, 1}; BigDecimal S51 = BroadhurstBbp(5, 1, a51, new MathContext(2 + mc.Precision)); BigDecimal S53 = BroadhurstBbp(5, 3, a53, new MathContext(2 + mc.Precision)); BigDecimal S55 = BroadhurstBbp(5, 5, a55, new MathContext(1 + mc.Precision)); S51 = S51.Multiply(new BigDecimal(18432)); S53 = S53.Multiply(new BigDecimal(14336)); S55 = S55.Multiply(new BigDecimal(1511424)); return S51.Add(S53).Subtract(S55).Divide(new BigDecimal(62651), mc); } /* Cohen et al Exp Math 1 (1) (1992) 25 */ var betsum = new Rational(); var bern = new Bernoulli(); var fact = new Factorial(); for (int npr = 0; npr <= (n + 1)/2; npr++) { Rational b = bern[2*npr].Multiply(bern[n + 1 - 2*npr]); b = b.Divide(fact[2*npr]).Divide(fact[n + 1 - 2*npr]); b = b.Multiply(1 - 2*npr); if (npr%2 == 0) betsum = betsum.Add(b); else betsum = betsum.Subtract(b); } betsum = betsum.Divide(n - 1); /* The first term, including the facor (2pi)^n, is essentially most * of the result, near one. The second term below is roughly in the range 0.003 to 0.009. * So the precision here is matching the precisionn requested by mc, and the precision * requested for 2*pi is in absolute terms adjusted. */ var mcloc = new MathContext(2 + mc.Precision + (int) (System.Math.Log10(n))); BigDecimal ftrm = PiRound(mcloc).Multiply(new BigDecimal(2)); ftrm = ftrm.Pow(n); ftrm = MultiplyRound(ftrm, betsum.ToBigDecimal(mcloc)); var exps = new BigDecimal(0); /* the basic accuracy of the accumulated terms before multiplication with 2 */ double eps = System.Math.Pow(10d, -mc.Precision); if (n%4 == 3) { /* since the argument n is at least 7 here, the drop * of the terms is at rather constant pace at least 10^-3, for example * 0.0018, 0.2e-7, 0.29e-11, 0.74e-15 etc for npr=1,2,3.... We want 2 times these terms * fall below eps/10. */ int kmax = mc.Precision/3; eps /= kmax; /* need an error of eps for 2/(exp(2pi)-1) = 0.0037 * The absolute error is 4*exp(2pi)*err(pi)/(exp(2pi)-1)^2=0.0075*err(pi) */ BigDecimal exp2p = PiRound(new MathContext(3 + ErrorToPrecision(3.14, eps/0.0075))); exp2p = Exp(exp2p.Multiply(new BigDecimal(2))); BigDecimal c = exp2p.Subtract(BigDecimal.One); exps = DivideRound(1, c); for (int npr = 2; npr <= kmax; npr++) { /* the error estimate above for npr=1 is the worst case of * the absolute error created by an error in 2pi. So we can * safely re-use the exp2p value computed above without * reassessment of its error. */ c = PowRound(exp2p, npr).Subtract(BigDecimal.One); c = MultiplyRound(c, (BigInteger.ValueOf(npr)).Pow(n)); c = DivideRound(1, c); exps = exps.Add(c); } } else { /* since the argument n is at least 9 here, the drop * of the terms is at rather constant pace at least 10^-3, for example * 0.0096, 0.5e-7, 0.3e-11, 0.6e-15 etc. We want these terms * fall below eps/10. */ int kmax = (1 + mc.Precision)/3; eps /= kmax; /* need an error of eps for 2/(exp(2pi)-1)*(1+4*Pi/8/(1-exp(-2pi)) = 0.0096 * at k=7 or = 0.00766 at k=13 for example. * The absolute error is 0.017*err(pi) at k=9, 0.013*err(pi) at k=13, 0.012 at k=17 */ BigDecimal twop = PiRound(new MathContext(3 + ErrorToPrecision(3.14, eps/0.017))); twop = twop.Multiply(new BigDecimal(2)); BigDecimal exp2p = Exp(twop); BigDecimal c = exp2p.Subtract(BigDecimal.One); exps = DivideRound(1, c); c = BigDecimal.One.Subtract(DivideRound(1, exp2p)); c = DivideRound(twop, c).Multiply(new BigDecimal(2)); c = DivideRound(c, n - 1).Add(BigDecimal.One); exps = MultiplyRound(exps, c); for (int npr = 2; npr <= kmax; npr++) { c = PowRound(exp2p, npr).Subtract(BigDecimal.One); c = MultiplyRound(c, (BigInteger.ValueOf(npr)).Pow(n)); BigDecimal d = DivideRound(1, exp2p.Pow(npr)); d = BigDecimal.One.Subtract(d); d = DivideRound(twop, d).Multiply(new BigDecimal(2*npr)); d = DivideRound(d, n - 1).Add(BigDecimal.One); d = DivideRound(d, c); exps = exps.Add(d); } } exps = exps.Multiply(new BigDecimal(2)); return ftrm.Subtract(exps, mc); }
public static BigDecimal Cot(BigDecimal x) { if (x.CompareTo(BigDecimal.Zero) == 0) { throw new ArithmeticException("Cannot take cot of zero " + x); } if (x.CompareTo(BigDecimal.Zero) < 0) { return Cot(x.Negate()).Negate(); } /* reduce modulo pi */ BigDecimal res = ModPi(x); /* absolute error in the result is err(x)/sin^2(x) to lowest order */ double xDbl = res.ToDouble(); double xUlpDbl = x.Ulp().ToDouble()/2d; double eps = xUlpDbl/2d/System.Math.Pow(System.Math.Sin(xDbl), 2d); BigDecimal xhighpr = ScalePrecision(res, 2); BigDecimal xhighprSq = MultiplyRound(xhighpr, xhighpr); var mc = new MathContext(ErrorToPrecision(xhighpr.ToDouble(), eps)); BigDecimal resul = BigDecimal.One.Divide(xhighpr, mc); /* x^(2i-1) */ BigDecimal xpowi = xhighpr; var b = new Bernoulli(); /* 2^(2i) */ var fourn = BigInteger.Parse("4"); /* (2i)! */ BigInteger fac = BigInteger.One; for (int i = 1;; i++) { Rational f = b[2*i]; fac = fac.Multiply(BigInteger.ValueOf((2*i))).Multiply(BigInteger.ValueOf((2*i - 1))); f = f.Multiply(fourn).Divide(fac); BigDecimal c = MultiplyRound(xpowi, f); if (i%2 == 0) resul = resul.Add(c); else resul = resul.Subtract(c); if (System.Math.Abs(c.ToDouble()) < 0.1*eps) break; fourn = fourn.ShiftLeft(2); xpowi = MultiplyRound(xpowi, xhighprSq); } mc = new MathContext(ErrorToPrecision(resul.ToDouble(), eps)); return resul.Round(mc); }
public static BigDecimal Tan(BigDecimal x) { if (x.CompareTo(BigDecimal.Zero) == 0) return BigDecimal.Zero; if (x.CompareTo(BigDecimal.Zero) < 0) { return Tan(x.Negate()).Negate(); } /* reduce modulo pi */ BigDecimal res = ModPi(x); /* absolute error in the result is err(x)/cos^2(x) to lowest order */ double xDbl = res.ToDouble(); double xUlpDbl = x.Ulp().ToDouble()/2d; double eps = xUlpDbl/2d/System.Math.Pow(System.Math.Cos(xDbl), 2d); if (xDbl > 0.8) { /* tan(x) = 1/cot(x) */ BigDecimal co = Cot(x); var mc = new MathContext(ErrorToPrecision(1d/co.ToDouble(), eps)); return BigDecimal.One.Divide(co, mc); } else { BigDecimal xhighpr = ScalePrecision(res, 2); BigDecimal xhighprSq = MultiplyRound(xhighpr, xhighpr); BigDecimal resul = xhighpr.Plus(); /* x^(2i+1) */ BigDecimal xpowi = xhighpr; var b = new Bernoulli(); /* 2^(2i) */ BigInteger fourn = BigInteger.ValueOf(4); /* (2i)! */ BigInteger fac = BigInteger.ValueOf(2); for (int i = 2;; i++) { Rational f = b[2*i].Abs(); fourn = fourn.ShiftLeft(2); fac = fac.Multiply(BigInteger.ValueOf((2*i))).Multiply(BigInteger.ValueOf((2*i - 1))); f = f.Multiply(fourn).Multiply(fourn.Subtract(BigInteger.One)).Divide(fac); xpowi = MultiplyRound(xpowi, xhighprSq); BigDecimal c = MultiplyRound(xpowi, f); resul = resul.Add(c); if (System.Math.Abs(c.ToDouble()) < 0.1*eps) break; } var mc = new MathContext(ErrorToPrecision(resul.ToDouble(), eps)); return resul.Round(mc); } }