コード例 #1
0
ファイル: BigMath.cs プロジェクト: tupunco/deveel-math
        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);
        }
コード例 #2
0
ファイル: BigMath.cs プロジェクト: tupunco/deveel-math
        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);
        }
コード例 #3
0
ファイル: BigMath.cs プロジェクト: tupunco/deveel-math
        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);
            }
        }