Esempio n. 1
0
        /// <summary>
        /// Returns the complete elliptic integral of the second kind D(k) = D(π/2,k)
        /// <para>D(k) = ∫ sin^2(θ)/sqrt(1-k^2*sin^2(θ)) dθ, θ={0,π/2}</para>
        /// </summary>
        /// <param name="k">The modulus. Requires |k| ≤ 1</param>
        public static double EllintD(double k)
        {
            if (!(k >= -1 && k <= 1))
            {
                Policies.ReportDomainError("EllintD(k: {0}): Requires |k| <= 1", k);
                return(double.NaN);
            }

            // special values
            if (k == 0)
            {
                return(Math.PI / 4);
            }

            if (Math.Abs(k) == 1)
            {
                return(Double.PositiveInfinity);
            }

            // http://dlmf.nist.gov/19.5#E3
            if (k * k < DoubleLimits.RootMachineEpsilon._13)
            {
                return((Math.PI / 4) * HypergeometricSeries.Sum2F1(1.5, 0.5, 2, k * k));
            }

            double x = 0;
            double y = 1 - k * k;
            double z = 1;

            double value = Math2.EllintRD(x, y, z) / 3;

            return(value);
        }
Esempio n. 2
0
        /// <summary>
        /// Returns the Spherical Bessel function of the first kind: j<sub>n</sub>(x)
        /// </summary>
        /// <param name="n">Order. Requires n &gt;= 0</param>
        /// <param name="x">Argument. Requires x &gt;= 0</param>
        /// <returns></returns>
        public static double SphBesselJ(int n, double x)
        {
            if (!(n >= 0) || !(x >= 0))
            {
                Policies.ReportDomainError("SphBesselJ(n: {0}, x: {1}): Requires n >= 0, x >= 0", n, x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                return(0);
            }

            //
            // Special case, n == 0 resolves down to the sinus cardinal of x:
            //
            if (n == 0)
            {
                return(Math2.Sinc(x));
            }
            if (x < 1)
            {
                // the straightforward evaluation below can underflow too quickly when x is small
                double result = (0.5 * Constants.SqrtPI);
                result *= (Math.Pow(x / 2, n) / Math2.Tgamma(n + 1.5));
                if (result != 0)
                {
                    result *= HypergeometricSeries.Sum0F1(n + 1.5, -x * x / 4);
                }
                return(result);
            }

            // Default case is just a straightforward evaluation of the definition:
            return(Constants.SqrtHalfPI * BesselJ(n + 0.5, x) / Math.Sqrt(x));
        }
Esempio n. 3
0
        /// <summary>
        /// Returns Log(1 + x) with improved accuracy for |x| ≤ 0.5
        /// </summary>
        /// <param name="x">The function argument. Requires x ≥ -1</param>
        public static double Log1p(double x)
        {
            if (!(x >= -1))
            {
                Policies.ReportDomainError("Log1p(x: {0}): Requires x >= -1", x);
                return(double.NaN);
            }
            if (x == -1)
            {
                return(double.NegativeInfinity);
            }
            if (double.IsInfinity(x))
            {
                return(double.PositiveInfinity);
            }


            double u = 1 + x;

            if (u == 1.0)
            {
                return(x);
            }

            return(Math.Log(u) * (x / (u - 1.0)));
        }
Esempio n. 4
0
        // In the implementation of these routines, use
        // parameters a1 = a1 + n instead of a1++; b1 = b1+n instead of b1++, etc
        // so the routines have a better chance of working when any a_n or b_n > 1/ϵ



        /// <summary>
        /// Returns the sum of a hypergeometric series 0F0. 
        /// <para>Sum0F0 = addend + Σ( (z^n/n!)) n={0,∞} = e^z</para>
        /// </summary>
        /// <param name="z">The argument</param>
        /// <param name="addend">The addend to the series</param>
        /// <returns>
        /// The sum of the series, or NaN if the series does not converge
        /// within the policy maximum number of iterations. 
        /// </returns>
        public static double Sum0F0(double z, double addend = 0)
        {
            if (double.IsNaN(z)
            || double.IsNaN(addend)) {
                Policies.ReportDomainError("Sum0F0(z: {0}, addend: {1}): NaN not allowed", z, addend);
                return double.NaN;
            }

            double sum = 1.0 + addend;
            if (z == 0)
                return sum;

            double term = 1.0;
            int n = 0;
            for (; n < Policies.MaxSeriesIterations; n++) {

                double prevSum = sum;
                term *= (z / (n + 1));
                sum += term;

#if CMP_FLOAT
                if (Math.Abs(term) <= Math.Abs(prevSum) * Policies.SeriesTolerance)
                    return sum;
#else
                if (sum == prevSum)
                    return sum;
#endif

            }

            Policies.ReportConvergenceError("Sum0F0(z: {0}, addend: {1}): No convergence after {2} iterations", z, addend, n);
            return double.NaN;
        }
Esempio n. 5
0
        /// <summary>
        /// Returns the Cylindrical Bessel J function: J<sub>v</sub>(x)
        /// </summary>
        /// <param name="v">Order</param>
        /// <param name="x">Argument</param>
        /// <returns></returns>
        public static double BesselJ(double v, double x)
        {
            if (double.IsNaN(x) || double.IsNaN(v)) {
                Policies.ReportDomainError("BesselJ(v: {0}, x: {1}): Requires finite arguments", v, x);
                return double.NaN;
            }
            if (x < 0 && !IsInteger(v)) {
                Policies.ReportDomainError("BesselJ(v: {0}, x: {1}): Requires integer v when x < 0", v, x);
                return double.NaN;
            }
            if (x == 0 && v < 0) {
                Policies.ReportDomainError("BesselJ(v: {0}, x: {1}): Complex results not supported. Requires v >= 0 when x = 0", v, x);
                return double.NaN;
            }

            if (double.IsInfinity(x))
                return 0;

            if (x == 0) {
                Debug.Assert(v >= 0);

                if (v == 0)
                    return 1;
                return 0;
            }

            if (IsInteger(v))
                return _Bessel.JN(v, x);

            Debug.Assert(x > 0);

            return _Bessel.JY(v, x, true, false).J;
        }
Esempio n. 6
0
        /// <summary>
        /// Returns the fraction where <paramref name="x" /> == fraction * 2 ^ exponent.
        /// <para>Note: |fraction| is in [0.5,1.0) or is 0</para>
        /// <para>if x is NaN or ±∞ then x is returned and the exponent is unspecified</para>
        /// </summary>
        /// <param name="x">Argument</param>
        /// <param name="exponent">Output the exponent value</param>
        /// <returns>System.Double.</returns>
        public static double Frexp(double x, out int exponent)
        {
            exponent = 0;

            // Check for +/- Inf or NaN
            if (double.IsNaN(x)) {
                Policies.ReportDomainError("Frexp(x: {0},...): NaNs not allowed", x);
                return x;
            }

            if (double.IsInfinity(x) || x == 0 ) 
                return x; // +/-0, +/-Inf


            IEEEDouble bits = new IEEEDouble(x);
            if (bits.IsSubnormal) {
                // Multiply by 2^52 to normalize the number
                const double normalMult = (1L << IEEEDouble.MantissaBits);
                bits = new IEEEDouble(x * normalMult);
                exponent = -IEEEDouble.MantissaBits;

                Debug.Assert(!bits.IsSubnormal);
            }

            // x is normal from here on

            // must return x in [0.5,1.0) = [2^-1,2^0) 
            // so: x_exponent must = -1 while keeping the mantissa and sign the same
            exponent += bits.ExponentValue + 1; // 2^0 = 1 = 0.5 * 2^1; so increment exponent

            // replace the exponent with 2^-1 so that x is in [0.5,1.0)
            const Int64 ExpHalf = 0x3fe0000000000000;
            return BitConverter.Int64BitsToDouble( (bits & ~IEEEDouble.ExponentMask)| ExpHalf );
        }
Esempio n. 7
0
        /// <summary>
        /// Returns the Cylindrical Bessel K function: K<sub>v</sub>(x)
        /// </summary>
        /// <param name="v">Order</param>
        /// <param name="x">Argument</param>
        /// <returns></returns>
        public static double BesselK(double v, double x)
        {
            if (!(x >= 0))
            {
                Policies.ReportDomainError("BesselK(v: {0}, x: {1}): Requires x >= 0", v, x);
                return(double.NaN);
            }
            if (x == 0)
            {
                if (v == 0)
                {
                    return(double.PositiveInfinity);
                }

                Policies.ReportDomainError("BesselK(v: {0}, x: {1}): Requires x > 0 when v != 0", v, x);
                return(double.NaN);
            }


            // K{-v} = K{v}
            // negative orders are handled in each of the following routines
            if (IsInteger(v) && (v > int.MinValue && v <= int.MaxValue))
            {
                return(_Bessel.KN((int)v, x));
            }

            double kv = _Bessel.IK(v, x, false, true).K;

            return(kv);
        }
Esempio n. 8
0
        /// <summary>
        /// Returns Cos(π * x)
        /// </summary>
        /// <param name="x">Argument</param>
        public static double CosPI(double x)
        {
            if (double.IsNaN(x) || double.IsInfinity(x))
            {
                Policies.ReportDomainError("CosPI(x: {0}): Requires finite x", x);
                return(double.NaN);
            }

            // cos(-x) == cos(x)
            if (x < 0)
            {
                x = -x;
            }

            // Reduce angles over 2*π
            if (x >= 2)
            {
                x -= 2 * Math.Floor(0.5 * x);
            }

            Debug.Assert(x >= 0 && x < 2);


            // reflect so that x in [0,1]*π
            // cos(x) = cos(2π-x)
            if (x >= 1.0)
            {
                x = 2.0 - x;
            }

            // -cos(x) = cos(π-x)
            bool neg = false;

            if (x > 0.5)
            {
                x   = 1 - x;
                neg = true;
            }

            if (x == 0.5)
            {
                return(0);
            }

            // using the identity: cos(x) = sin(π/2-x)
            // when x > 0.25 significantly improves the precision of this routine in .NET

            double result;

            if (x > 0.25)
            {
                result = Math.Sin((0.5 - x) * Math.PI);
            }
            else
            {
                result = Math.Cos(Math.PI * x);
            }

            return((neg) ? -result : result);
        }
Esempio n. 9
0
        /// <summary>
        /// Returns ∂P(a,x)/∂x = e^(-x) * x^(a-1) / Γ(a)
        /// <para>Note: ∂Q(a,x)/∂x = -∂P(a,x)/∂x</para>
        /// </summary>
        /// <param name="a">Requires finite a &gt; 0</param>
        /// <param name="x">Requires x ≥ 0</param>
        public static double GammaPDerivative(double a, double x)
        {
            if ((!(a > 0) || double.IsInfinity(a)) ||
                (!(x >= 0)))
            {
                Policies.ReportDomainError("GammaPDerivative(a: {0}, x: {1}): Requires finite a > 0; x >= 0", a, x);
                return(double.NaN);
            }

            //
            // Now special cases:
            //
            if (x == 0)
            {
                if (a > 1)
                {
                    return(0);
                }
                if (a == 1)
                {
                    return(1);
                }
                return(double.PositiveInfinity);
            }

            // lim {x->+inf} e^(-x + (a-1)*ln(x)) / Γ(a) = 0/Γ(a) = 0
            if (double.IsInfinity(x))
            {
                return(0);
            }

            // when x is small, we can underflow too quickly
            // For x < eps, e^-x ~= 1, so use
            //      x^(a-1)/Γ(a)
            // and for eps < x < 1, use:
            //      IgammaPrefixRegularized(a, x) = (x/(a-1)) * IgammaPrefixRegularized(a-1, x)

            if (x <= DoubleLimits.MachineEpsilon)
            {
                if (a < DoubleLimits.MachineEpsilon / Constants.EulerMascheroni)
                {
                    return((a / x) * Math.Pow(x, a));
                }
                if (a < 1)
                {
                    return(Math.Pow(x, a) / (x * Math2.Tgamma(a)));
                }
                return(Math.Pow(x, a - 1) / Math2.Tgamma(a));
            }

            if (x < 1 && a >= 2)
            {
                return(_Igamma.PrefixRegularized(a - 1, x) / (a - 1));
            }

            // Normal case
            double result = _Igamma.PrefixRegularized(a, x) / x;

            return(result);
        }
Esempio n. 10
0
        /// <summary>
        /// Returns the value "a" such that q == GammaQ(a, x);
        /// </summary>
        /// <param name="x">Requires x ≥ 0</param>
        /// <param name="q">Requires 0 ≤ q ≤ 1</param>
        public static double GammaQInvA(double x, double q)
        {
            if ((!(x >= 0) || double.IsInfinity(x)) ||
                (!(q >= 0 && q <= 1)))
            {
                Policies.ReportDomainError("GammaPInvA(x: {0}, q: {1}): Requires finite x >= 0; q in [0,1]", x, q);
                return(double.NaN);
            }

            if (q == 0)
            {
                return(DoubleLimits.MinNormalValue);
            }
            if (q == 1)
            {
                return(double.MaxValue);
            }

            var p = 1 - q;

            if (q <= 0.5)
            {
                return(_GammaInvA.ImpQ(x, p, q));
            }

            return(_GammaInvA.ImpP(x, p, q));
        }
Esempio n. 11
0
        /// <summary>
        /// Returns the value "a" such that: p == GammaP(a, x);
        /// </summary>
        /// <param name="x">Requires x ≥ 0</param>
        /// <param name="p">Requires 0 ≤ p ≤ 1</param>
        public static double GammaPInvA(double x, double p)
        {
            if ((!(x >= 0) || double.IsInfinity(x)) ||
                (!(p >= 0 && p <= 1)))
            {
                Policies.ReportDomainError("GammaPInvA(x: {0}, p: {1}): Requires finite x >= 0; p in [0,1]", x, p);
                return(double.NaN);
            }

            if (p == 0)
            {
                return(double.MaxValue);
            }
            if (p == 1)
            {
                return(DoubleLimits.MinNormalValue);
            }

            if (p < 0.5)
            {
                return(_GammaInvA.ImpP(x, p, 1 - p));
            }

            return(_GammaInvA.ImpQ(x, p, 1 - p));
        }
Esempio n. 12
0
        /// <summary>
        /// Returns Floor(Log(2,|<paramref name="x"/>|)).
        /// Note: this is the exponent field of a double precision number.
        /// <para>If x is NaN then x is returned</para>
        /// <para>If x == ±∞ then +∞ is returned</para>
        /// <para>If x == ±0 then -∞ is returned</para>
        /// </summary>
        /// <param name="x">Argument</param>
        public static double Logb(double x)
        {
            IEEEDouble rep = new IEEEDouble(x);

            // Check for +/- Inf or NaN
            if (!rep.IsFinite) {
                if ( rep.IsInfinity )
                    return double.PositiveInfinity;
                Policies.ReportDomainError("Logb(x: {0}): NaNs not allowed", x);
                return x;
            }

            if ( x == 0 ) {
                Policies.ReportPoleError("Logb(x: {0}): Logb(0) == -∞", x);
                return double.NegativeInfinity;

            }

            int exponent = 0;
            if (rep.IsSubnormal) {
                // Multiply by 2^53 to normalize the number
                const double normalMult = (1L << IEEEDouble.MantissaBits);
                rep = new IEEEDouble(x * normalMult);
                exponent = -IEEEDouble.MantissaBits;

                Debug.Assert(!rep.IsSubnormal);
            }

            exponent += rep.ExponentValue; 
            return exponent;
        }
Esempio n. 13
0
        /// <summary>
        /// Returns the sum of a hypergeometric series 3F0 with one numerator = 1. 
        /// <para>Sum3F0_A1 = addend + Σ(( (a1)_n * (a2)_n ) * (z^n)) n={0,∞}</para>
        /// </summary>
        /// <param name="a1">First numerator</param>
        /// <param name="a2">Second numerator</param>
        /// <param name="z">Argument</param>
        /// <param name="addend">The addend to the series</param>
        /// <returns>
        /// The sum of the series, or NaN if the series does not converge
        /// within the policy maximum number of iterations 
        /// </returns>
        private static double Sum3F0_A1(double a1, double a2, double z, double addend)
        {
            if ((double.IsNaN(a1) || double.IsInfinity(a1))
            || (double.IsNaN(a2) || double.IsInfinity(a2))
            || (double.IsNaN(z) || double.IsInfinity(z))
            || double.IsNaN(addend)) {
                Policies.ReportDomainError("Sum3F1(a1: {0}, a2: {1}, z: {2}, addend: {3}): Requires finite arguments", a1, a2, z, addend);
                return double.NaN;
            }

            double sum = 1.0 + addend;

            double term = 1.0;
            int n = 0; 
            for (; n < Policies.MaxSeriesIterations; n++) {

                double prevSum = sum;
                term *= z * (a1 + n) * (a2 + n);
                sum += term;

#if CMP_FLOAT
                if (Math.Abs(term) <= Math.Abs(prevSum) * Policies.SeriesTolerance)
                    return sum;
#else
                if (sum == prevSum)
                    return sum;
#endif
            }

            Policies.ReportConvergenceError("Sum3F1(a1: {0}, a2: {1}, z: {2}, addend: {3}): No convergence after {4} iterations", a1, a2, z, addend, n);
            return double.NaN;
        }
Esempio n. 14
0
        /// <summary>
        /// Returns the value of the Riemann Zeta function at <paramref name="x"/>
        /// <para>ζ(x) = Σ n^-x, n={1, ∞}</para>
        /// </summary>
        /// <param name="x">The function argument</param>
        public static double Zeta(double x)
        {
            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("Zeta(x: {0}): NaN not allowed", x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                if (x > 0)
                {
                    return(1);
                }

                Policies.ReportDomainError("Zeta(x: {0}): -Infinity not allowed", x);
                return(double.NaN);
            }
            if (x == 1)
            {
                Policies.ReportPoleError("Zeta(x: {0}): Evaluation at pole", x);
                return(double.NaN);
            }

            return(_Zeta.Imp(x, 1 - x));
        }
Esempio n. 15
0
        /// <summary>
        /// Returns the Spherical Bessel function of the second kind: y<sub>n</sub>(x)
        /// </summary>
        /// <param name="v">Order. Requires n &gt;= 0</param>
        /// <param name="x">Argument. Requires x &gt;= 0</param>
        /// <returns></returns>
        public static double SphBesselY(int v, double x)
        {
            const double LowerLimit = 2 * DoubleLimits.MinNormalValue;

            if (!(v >= 0) || !(x >= 0))
            {
                Policies.ReportDomainError("SphBesselY(v: {0}, x: {1}): Requires v >= 0, x >= 0", v, x);
                return(double.NaN);
            }
            if (x < LowerLimit)
            {
                return(double.NegativeInfinity);
            }
            if (double.IsInfinity(x))
            {
                return(0);
            }

            // The code below is the following
            // double result = (Constants.SqrtHalfPI/Math.Sqrt(x)) * Math2.CylBesselY(v+0.5, x);
            // but for x << v, spherical Bessel y can overflow too soon.
            var(J, Y) = _Bessel.JY(v + 0.5, x, false, true);
            double prefix = (Constants.SqrtHalfPI / Math.Sqrt(x));
            double result = (double)(prefix * Y);

            return(result);
        }
Esempio n. 16
0
        /// <summary>
        /// Returns "a" such that: I<sub>x</sub>(a, b) == p
        /// </summary>
        /// <param name="b">Requires b &gt; 0</param>
        /// <param name="x">Requires 0 ≤ x ≤ 1</param>
        /// <param name="p">Requires 0 ≤ p ≤ 1</param>
        public static double IbetaInvA(double b, double x, double p)
        {
            if ((!(b > 0) || double.IsInfinity(b)) ||
                (!(x >= 0 && x <= 1)) ||
                (!(p >= 0 && p <= 1)))
            {
                Policies.ReportDomainError("IbetaInvA(b: {0}, x: {1}, p: {2}): Requires finite b > 0; x,p in [0,1]", b, x, p);
                return(double.NaN);
            }

            if (p == 1)
            {
                return(0);
            }
            if (p == 0)
            {
                return(double.PositiveInfinity);
            }

            // use I_x(a,b) = 1 - I_{1-x}(b, a)
            if (p > 0.5 && x > 0.5)
            {
                return(_IbetaInvAB.IbetaInvBImp(b, 1 - x, x, 1 - p, p));
            }


            return(_IbetaInvAB.IbetaInvAImp(b, x, 1 - x, p, 1 - p));
        }
Esempio n. 17
0
        /// <summary>
        /// Returns "b" such that: 1 - I<sub>x</sub>(a, b) == q
        /// </summary>
        /// <param name="a">Requires a ≥ 0</param>
        /// <param name="x">Requires 0 ≤ x ≤ 1</param>
        /// <param name="q">Requires 0 ≤ q ≤ 1</param>
        public static double IbetacInvB(double a, double x, double q)
        {
            if ((!(a > 0) || double.IsInfinity(a)) ||
                (!(x >= 0 && x <= 1)) ||
                (!(q >= 0 && q <= 1)))
            {
                Policies.ReportDomainError("IbetaInvB(a: {0}, x: {1}, q: {2}): Requires finite a > 0; x,q in [0,1]", a, x, q);
                return(double.NaN);
            }

            if (q == 0)
            {
                return(double.PositiveInfinity);
            }
            if (q == 1)
            {
                return(0);
            }

            // use I_x(a,b) = 1 - I_{1-x}(b, a)
            if (q > 0.5 && x > 0.5)
            {
                return(_IbetaInvAB.IbetaInvAImp(a, 1 - x, x, q, 1 - q));
            }

            return(_IbetaInvAB.IbetaInvBImp(a, x, 1 - x, 1 - q, q));
        }
Esempio n. 18
0
        /// <summary>
        /// Evaluates the first n terms of polynomial with a given argument
        /// <para>Σ( c[k] * x^k ) k={0, nTerms-1}</para>
        /// </summary>
        /// <param name="c">The coefficient array</param>
        /// <param name="x">The polynomial argument</param>
        /// <param name="nTerms">The maximum number of terms to evaluate</param>
        public static double Eval(double[] c, double x, int nTerms)
        {
            // Uses Horner's Method
            // see http://en.wikipedia.org/wiki/Horner_scheme

            if (c == null)
            {
                Policies.ReportDomainError("Requires Coefficients (c) != null");
                return(double.NaN);
            }
            if (nTerms < 0 || nTerms > c.Length)
            {
                Policies.ReportDomainError("Requires nTerms in [0, {0}]; nTerms = {1}", c.Length - 1, nTerms);
                return(double.NaN);
            }

            if (nTerms == 0)
            {
                return(0);
            }

            int    n      = nTerms - 1;
            double result = c[n];

            for (int i = n - 1; i >= 0; i--)
            {
                result = result * x + c[i];
            }
            return(result);
        }
Esempio n. 19
0
        /// <summary>
        /// Returns <paramref name="x"/> * 2^n
        /// </summary>
        /// <param name="x">Argument</param>
        /// <param name="n">The binary exponent (2^n)</param>
        /// <returns>
        /// Returns <paramref name="x"/> * 2^n
        /// <para>If <paramref name="x"/> is NaN, returns NaN.</para> 
        /// <para>If <paramref name="x"/> is ±0 or ±∞, returns x.</para> 
        /// <para>If n is 0, returns x.</para>
        /// </returns> 
        public static double Ldexp(double x, int n)
        {
            const double toNormal = (1L << IEEEDouble.MantissaBits);
            const double fromNormal = 1.0/(1L << IEEEDouble.MantissaBits);

            // handle special cases
            if (double.IsNaN(x)) {
                Policies.ReportDomainError("Ldexp(x: {0}, n: {1}): Requires x not NaN", x, n);
                return x;
            }
            if (double.IsInfinity(x))
                return x; // +/-Inf

            if (x == 0 || n == 0)
                return x; // +/-0, x

            IEEEDouble bits = new IEEEDouble(x);
            int xExp = 0;
            if ( bits.IsSubnormal ) {
                bits = new IEEEDouble(x * toNormal);
                xExp -= IEEEDouble.MantissaBits;
            }
            xExp += bits.ExponentValue + n;

            // there will be an overflow
            if (xExp > IEEEDouble.MaxBinaryExponent) 
                return (x > 0) ? double.PositiveInfinity : double.NegativeInfinity;
            
            // cannot represent with a denorm -- underflow
            if (xExp < IEEEDouble.MinBinaryDenormExponent) 
                return (x > 0) ? 0.0 : -0.0;
            
            if (xExp >= IEEEDouble.MinBinaryExponent) {
                // in the normal range - just set the exponent to the sum
                bits.ExponentValue = xExp;
                return bits;
            }

            // We have a denormalized number

#if true
            // Use C99 definition x*2^exp
            xExp += IEEEDouble.MantissaBits;
            Debug.Assert(xExp >= IEEEDouble.MinBinaryExponent);

            bits.ExponentValue = xExp;
            return ((double)bits) * fromNormal;
#else
            // Make it compatible with MS C library - no rounding for denorms
            // kept for future reference

            int shift = IEEEDouble.MinBinaryExponent - xExp;
            Debug.Assert(shift > 0);

            // Recover the hidden mantissa bit and shift
            const Int64 impliedbit = 1L << IEEEDouble.MantissaBits; // 1.MANTISSA = 1 << 52
            Int64 mantissa = (bits.Significand | impliedbit) >> shift;
            return BitConverter.Int64BitsToDouble(bits.Sign | mantissa);
#endif
        }
Esempio n. 20
0
        /// <summary>
        /// Returns the sum of a series with each term given by the function f().
        /// </summary>
        /// <param name="f">Delegate returning the next term</param>
        /// <param name="initialValue">initial value</param>
        /// <param name="tolerance">Stops when the relative tolerance is reached</param>
        /// <param name="maxIterations">The maximum number of terms</param>
        /// <returns>
        /// The sum of the series or NaN if the series does not converge within the maximum
        /// number of iterations specified in the policy.
        /// </returns>
        public static double Eval(Func <double> f, double initialValue, double tolerance, uint maxIterations)
        {
            if (f == null)
            {
                Policies.ReportDomainError("Requires f != null");
                return(double.NaN);
            }
            if (!(tolerance >= 0))
            {
                Policies.ReportDomainError("Requires relative tolerance >= 0");
                return(double.NaN);
            }


            double sum = initialValue;

            for (UInt32 i = 0; i < maxIterations; i++)
            {
                double prevSum = sum;
                double delta   = f();
                sum += delta;

                if (Math.Abs(delta) <= Math.Abs(prevSum) * tolerance)
                {
                    return(sum);
                }
            }

            Policies.ReportConvergenceError("Series did not converge in {0} iterations", maxIterations);
            return(double.NaN);
        }
Esempio n. 21
0
        /// <summary>
        /// Returns the value of the Airy Ai function at <paramref name="x"/>
        /// </summary>
        /// <param name="x">The function argument</param>
        /// <returns></returns>
        public static double AiryAi(double x)
        {
            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("AiryAi(x: {0}): NaN not allowed", x);
                return(double.NaN);
            }
            if (double.IsInfinity(x))
            {
                return(0);
            }

            double absX = Math.Abs(x);

            if (x >= -3 && x <= 2)
            {
                // Use small series. See:
                // http://functions.wolfram.com/Bessel-TypeFunctions/AiryAi/06/01/02/01/0004/
                // http://dlmf.nist.gov/9.4

                double z  = x * x * x / 9;
                double s1 = Constants.AiryAi0 * HypergeometricSeries.Sum0F1(2 / 3.0, z);
                double s2 = x * Constants.AiryAiPrime0 * HypergeometricSeries.Sum0F1(4 / 3.0, z);
                return(s1 + s2);
            }

            const double v    = 1.0 / 3;
            double       zeta = 2 * absX * Math.Sqrt(absX) / 3;

            if (x < 0)
            {
                if (x < -32)
                {
                    return(AiryAsym.AiBiNeg(x).Ai);
                }

                // The following is
                //double j1 = Math2.BesselJ(v, zeta);
                //double j2 = Math2.BesselJ(-v, zeta);
                //double ai = Math.Sqrt(-x) * (j1 + j2) / 3;

                var(J, Y) = _Bessel.JY(v, zeta, true, true);
                double s  = 0.5 * (J - ((double)Y) / Constants.Sqrt3);
                double ai = Math.Sqrt(-x) * s;

                return(ai);
            }
            else
            {
                // Use the K relationship: http://dlmf.nist.gov/9.6

                if (x >= 16)
                {
                    return(AiryAsym.Ai(x));
                }

                double ai = Math2.BesselK(v, zeta) * Math.Sqrt(x / 3) / Math.PI;
                return(ai);
            }
        }
Esempio n. 22
0
        /// <summary>
        /// Returns the next representable value which is greater than x.
        /// </summary>
        /// <param name="x">FloatNext argument</param>
        /// <returns>The next floating point value
        /// <para>If x == NaN || x == Infinity, returns NaN</para>
        /// <para>If x == MaxValue, returns PositiveInfinity</para></returns>
        static public double FloatNext(double x)
        {
            if (double.IsNaN(x) || double.IsInfinity(x))
            {
                Policies.ReportDomainError("FloatNext(x: {0}): Requires finite x", x);
                return(double.NaN);
            }

            if (x >= double.MaxValue)
            {
                return(double.PositiveInfinity);
            }


            if (x == 0)
            {
                return(double.Epsilon);
            }

            Int64 bits = BitConverter.DoubleToInt64Bits(x);

            // check if we should increase or decrease the mantissa
            if (x >= 0)
            {
                bits++;
            }
            else
            {
                bits--;
            }

            return(BitConverter.Int64BitsToDouble(bits));
        }
Esempio n. 23
0
        /// <summary>
        /// Evaluates a finite continued fraction
        /// <para>Return = initialValue + a[0]/(b[0]+(a[1]/b[1]+...(b[n-1]+a[n]/b[n])))</para>
        /// </summary>
        /// <param name="initialValue">The initial value is added to the finite fraction</param>
        /// <param name="a">The array of numerators</param>
        /// <param name="b">The array of denominators</param>
        /// <returns></returns>
        public static double Eval(double initialValue, double[] a, double[] b)
        {
            if (double.IsNaN(initialValue))
            {
                Policies.ReportDomainError("Requires finite initialValue: initialValue = {0}", initialValue);
                return(double.NaN);
            }
            if (a == null)
            {
                Policies.ReportDomainError("Requires a != null");
                return(double.NaN);
            }
            if (b == null)
            {
                Policies.ReportDomainError("Requires b != null");
                return(double.NaN);
            }
            if (a.Length != b.Length)
            {
                Policies.ReportDomainError("Requires equal sized arrays: a.Length = {0}; b.Length = {1}", a.Length, b.Length);
                return(double.NaN);
            }

            IEnumerable <(double, double)> series = a.Zip(b, (x, y) => (x, y));

            return(Eval(initialValue, series, _DefaultTolerance, a.Length));
        }
Esempio n. 24
0
        /// <summary>
        /// Returns the partial derivative of IBeta(a,b,x) with respect to <paramref name="x"/>
        /// <para>IbetaDerivative(a, b, x) = ∂( I<sub>x</sub>(a,b) )/∂x = (1-x)^(b-1)*x^(a-1)/B(a,b)</para>
        /// </summary>
        /// <param name="a">Requires a ≥ 0 and not both a and b are zero</param>
        /// <param name="b">Requires b ≥ 0 and not both a and b are zero</param>
        /// <param name="x">Defined for 0 ≤ x ≤ 1</param>
        public static double IbetaDerivative(double a, double b, double x)
        {
            if (!(a > 0) ||
                !(b > 0) ||
                !(x >= 0 && x <= 1))
            {
                Policies.ReportDomainError("IbetaDerivative(a: {0}, b: {1}, x: {2}): Requires a,b >= 0; x in [0,1]", a, b, x);
                return(double.NaN);
            }

            //
            // Now the corner cases:
            //
            if (x == 0)
            {
                if (a > 1)
                {
                    return(0);
                }
                if (a == 1)
                {
                    return(1 / Math2.Beta(a, b));
                }

                return(double.PositiveInfinity);
            }
            if (x == 1)
            {
                if (b > 1)
                {
                    return(0);
                }
                if (b == 1)
                {
                    return(1 / Math2.Beta(a, b));
                }

                return(double.PositiveInfinity);
            }

            // handle denorm x
            if (x < DoubleLimits.MinNormalValue)
            {
                double logValue = a * Math.Log(x) + b * Math2.Log1p(-x) - Math.Log(x * (1 - x));
                logValue -= Math2.LogBeta(a, b);
                double result = Math.Exp(logValue);
#if EXTRA_DEBUG
                Debug.WriteLine("IbetaDerivative(a: {0}, b: {1}, x: {2}): Denorm = {3}", a, b, x, result);
#endif
                return(result);
            }

            //
            // Now the regular cases:
            //
            double mult = (1 - x) * x;
            double f1   = _Ibeta.PowerTerms(a, b, x, 1 - x, true, 1 / mult);

            return(f1);
        }
Esempio n. 25
0
        // Carlson's degenerate elliptic integral
        // Carlson, Numerische Mathematik, vol 33, 1 (1979)


        /// <summary>
        /// Carlson's symmetric form of elliptical integrals
        /// <para>R<sub>C</sub>(x,y) = R<sub>F</sub>(x,y,y) = (1/2) * ∫ dt/((t+y)*Sqrt(t+x)), t={0,∞}</para>
        /// </summary>
        public static double EllintRC(double x, double y)
        {
            if ((!(x >= 0) || double.IsInfinity(x)) ||
                (y == 0 || double.IsInfinity(y) || double.IsNaN(y)))
            {
                Policies.ReportDomainError("EllintRC(x: {0}, y: {1}): Requires finite x >= 0; finite y != 0", x, y);
                return(double.NaN);
            }

            // Taylor series: Max Value
            const double tolerance = 0.003100392679625389599124425076703727071077135406054400409781;

            Debug.Assert(Math2.AreNearUlps(tolerance, Math.Pow(4 * DoubleLimits.MachineEpsilon, 1.0 / 6), 5), "Incorrect constant");

            // for y < 0, the integral is singular, return Cauchy principal value
            double prefix = 1;

            if (y < 0)
            {
                prefix = Math.Sqrt(x / (x - y));
                x     -= y;
                y      = -y;
            }

            if (x == 0)
            {
                return(prefix * ((Math.PI / 2) / Math.Sqrt(y)));
            }
            if (x == y)
            {
                return(prefix / Math.Sqrt(x));
            }

            int k = 1;

            for (; k < Policies.MaxSeriesIterations; k++)
            {
                double u = (x + y + y) / 3;
                double S = y / u - 1; // 1 - x / u = 2 * S

                if (2 * Math.Abs(S) < tolerance)
                {
                    // Taylor series expansion to the 5th order
                    double value = (1 + S * S * (3.0 / 10 + S * (1.0 / 7 + S * (3.0 / 8 + S * (9.0 / 22))))) / Math.Sqrt(u);
                    return(value * prefix);
                }

                double sx     = Math.Sqrt(x);
                double sy     = Math.Sqrt(y);
                double lambda = 2 * sx * sy + y;
                x = (x + lambda) / 4;
                y = (y + lambda) / 4;
            }

            Policies.ReportDomainError("EllintRC(x: {0}, y: {1}): No convergence after {2} iterations", x, y, k);
            return(double.NaN);
        }
Esempio n. 26
0
        /// <summary>
        /// Returns the Jacobi Zeta function
        /// Z(k, φ) = E(k, φ) - E(k)/K(k)*F(k, φ)
        /// </summary>
        /// <param name="k">The modulus. Requires |k| ≤ 1</param>
        /// <param name="phi">The amplitude</param>
        /// <returns></returns>
        public static double JacobiZeta(double k, double phi)
        {
            if ((!(k >= -1 && k <= 1)) || double.IsNaN(phi) || double.IsInfinity(phi))
            {
                Policies.ReportDomainError("JacobiZeta(k: {0}, phi: {1}): Requires |k| <= 1; finite phi", k, phi);
                return(double.NaN);
            }

            if (phi == 0)
            {
                return(0);
            }

            double sign = 1;

            if (phi < 0)
            {
                phi  = Math.Abs(phi);
                sign = -1;
            }

            double k2 = k * k;

            if (k2 < 8 * DoubleLimits.MachineEpsilon)
            {
                // See: http://functions.wolfram.com/EllipticIntegrals/JacobiZeta/06/01/08/0001/
                if (k2 == 0)
                {
                    return(0);
                }
                return(sign * Math.Sin(2 * phi) * k2 / 4);
            }

            double sinp = Math.Sin(phi);
            double cosp = Math.Cos(phi);

            if (k == 1)
            {
                // We get here by simplifying JacobiZeta[w, 1] in Mathematica, and the fact that 0 <= phi.
                return(sign * sinp * Math.Sign(cosp));
            }

            double x = 0;
            double y = 1 - k2;
            double z = 1;

            double p = 1 - k2 * sinp * sinp;

            if (p < 0.125)   // second form is more accurate for small p
            {
                p = (1 - k2) + k2 * cosp * cosp;
            }

            double result = sign * k2 * sinp * cosp * Math.Sqrt(p) * EllintRJ(x, y, z, p) / (3 * EllintK(k));

            return(result);
        }
Esempio n. 27
0
        /// <summary>
        /// Returns the value "x" such that q == GammaQ(a, x);
        /// </summary>
        /// <param name="a">Requires a > 0</param>
        /// <param name="q">Requires 0 ≤ q ≤ 1</param>
        public static double GammaQInv(double a, double q)
        {
            if ((!(a > 0) || double.IsInfinity(a)) ||
                (!(q >= 0 && q <= 1)))
            {
                Policies.ReportDomainError("GammaQInv(a: {0}, q: {1}): Requires finite a > 0; q in [0,1]", a, q);
                return(double.NaN);
            }

            if (q == 0)
            {
                return(double.PositiveInfinity);
            }
            if (q == 1)
            {
                return(0);
            }

            // Q(1,x) = e^-x
            if (a == 1)
            {
                return(-Math.Log(q));
            }
            // Q(1/2, x) = erfc(sqrt(x))
            if (a == 0.5)
            {
                return(Squared(Math2.ErfcInv(q)));
            }


            double guess = _IgammaInv.GammaPInvGuess(a, 1 - q, q, out bool has10Digits);

            var(lower, upper) = _IgammaInv.GammaQInvLimits(a, q);

            // there can be some numerical uncertainties in the limits,
            // particularly for denormalized values, so adjust the limits
            if (upper == 0 && lower == 0)
            {
                return(0.0);
            }
            if (guess <= DoubleLimits.MinNormalValue)
            {
                return(guess);
            }
            if (guess <= lower)
            {
                lower = DoubleLimits.MinNormalValue;
            }
            if (guess > upper)
            {
                upper = guess;
            }


            return(_IgammaInv.SolveGivenAQ(a, q, guess, lower, upper));
        }
Esempio n. 28
0
        /// <summary>
        /// Returns Sin(π * x)
        /// </summary>
        /// <param name="x">Argument</param>
        public static double SinPI(double x)
        {
            if (double.IsNaN(x) || double.IsInfinity(x))
            {
                Policies.ReportDomainError("SinPI(x: {0}): Requires finite x", x);
                return(double.NaN);
            }

            // sin(-x) == -sin(x)
            bool neg = false;

            if (x < 0)
            {
                x   = -x;
                neg = !neg;
            }

            // Reduce angles over 2*PI
            if (x >= 2)
            {
                x -= 2 * Math.Floor(x / 2);
            }

            Debug.Assert(x >= 0 && x < 2);

            // reflect in the x-axis
            // and negate so that x in [0,1]*π
            if (x > 1)
            {
                x   = 2.0 - x;
                neg = !neg;
            }

            // reflect in the y-axis so that x in [0,0.5]*π
            if (x > 0.5)
            {
                x = 1.0 - x;
            }

            double result;

            if (x == 0.5)
            {
                result = 1.0;
            }
            else if (x > 0.25)
            {
                result = Math.Cos((0.5 - x) * Math.PI);
            }
            else
            {
                result = Math.Sin(Math.PI * x);
            }

            return((neg) ? -result : result);
        }
Esempio n. 29
0
        /// <summary>
        /// Returns the sum of a hypergeometric series 3F1. 
        /// <para>Sum3F1 = addend + Σ(( (a1)_n * (a2)_n * (a3)_n)/(b1)_n * (z^n/n!)) n={0,∞}</para>
        /// </summary>
        /// <param name="a1">First numerator</param>
        /// <param name="a2">Second numerator</param>
        /// <param name="a3">Third numerator</param>
        /// <param name="b1">First denominator. Requires b1 > 0</param>
        /// <param name="z">Argument</param>
        /// <param name="addend">The addend to the series</param>
        /// <returns>
        /// The sum of the series, or NaN if the series does not converge
        /// within the policy maximum number of iterations 
        /// </returns>
        public static double Sum3F1(double a1, double a2, double a3, double b1, double z, double addend = 0)
        {
            if ((double.IsNaN(a1) || double.IsInfinity(a1))
            || (double.IsNaN(a2) || double.IsInfinity(a2))
            || (double.IsNaN(a3) || double.IsInfinity(a3))
            || (double.IsNaN(z) || double.IsInfinity(z))
            || (double.IsNaN(b1) || double.IsInfinity(b1))
            || double.IsNaN(addend)) {
                Policies.ReportDomainError("Sum3F1(a1: {0}, a2: {1}, a3: {2}, b1: {3}, z: {4}, addend: {5}): Requires finite arguments", a1, a2, a3, b1, z, addend);
                return double.NaN;
            }
            if (b1 <= 0 && Math2.IsInteger(b1)) {
                Policies.ReportDomainError("Sum3F1(a1: {0}, a2: {1}, a3: {2}, b1: {3}, z: {4}, addend: {5}): Requires b1 is not zero or a negative integer", a1, a2, a3, b1, z, addend);
                return double.NaN;
            }

            double sum = 1.0 + addend;

            if (z == 0)
                return sum;
            if (a1 == b1)
                return Sum2F0(a2, a3, z, addend);
            if (a2 == b1)
                return Sum2F0(a1, a3, z, addend);
            if (a3 == b1)
                return Sum2F0(a1, a2, z, addend);
#if false
            // TODO:
            if (a1 == 1)
                return Sum3F1_A1(a2, a3, b1, z, addend);
            if (a2 == 1)
                return Sum3F1_A1(a1, a3, b1, z, addend);
            if (a3 == 1)
                return Sum3F1_A1(a1, a2, b1, z, addend);
#endif

            double term = 1.0;
            int n = 0; 
            for (; n < Policies.MaxSeriesIterations; n++) {

                double prevSum = sum;
                term *= (z / (n + 1)) * ((a1 + n) / (b1 + n)) * (a2 + n) * (a3 + n);
                sum += term;

#if CMP_FLOAT
                if (Math.Abs(term) <= Math.Abs(prevSum) * Policies.SeriesTolerance)
                    return sum;
#else
                if (sum == prevSum)
                    return sum;
#endif
            }

            Policies.ReportConvergenceError("Sum3F1(a1: {0}, a2: {1}, a3: {2}, b1: {3}, z: {4}, addend: {5}): No convergence after {6} iterations", a1, a2, a3, b1, z, addend, n);
            return double.NaN;
        }
Esempio n. 30
0
        /// <summary>
        /// Returns the Scaled Complementary Error Function
        /// <para>Erfcx(x) = e^(x^2)*Erfc(x)</para>
        /// </summary>
        /// <param name="x">The Argument</param>
        /// <returns></returns>
        public static double Erfcx(double x)
        {
            if (double.IsNaN(x))
            {
                Policies.ReportDomainError("Erfcx(x: {0}): NaN not allowed", x);
                return(double.NaN);
            }

            return(Erf_Imp(x, true, true));
        }