/// <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)); }
/// <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; }
/// <summary> /// Returns the value of the Riemann Zeta function at <paramref name="n"/> /// <para>ζ(n) = Σ k^-n, k={1, ∞}</para> /// </summary> /// <param name="n">The function argument</param> public static double Zeta(int n) { if (n == 1) { Policies.ReportPoleError("Zeta(n: {0}): Evaluation at pole", n); return(double.NaN); } return(_Zeta.Imp(n)); }
/// <summary> /// Returns the Exponential Integral /// <para>E<sub>n</sub>(x) = ∫ e^(-x*t)/t^n dt, t={1,∞}</para> /// </summary> /// <param name="n">Requires n ≥ 0</param> /// <param name="x">Requires x ≥ 0</param> public static double Expint(int n, double x) { if (!(n >= 0) || !(x >= 0)) { Policies.ReportDomainError("Expint(n: {0}, x: {1}): Requires n,x >= 0", n, x); return(double.NaN); } // E_{n}(∞) = 0 // see: http://functions.wolfram.com/06.34.03.0014.01 if (double.IsInfinity(x)) { return(0); } if (x == 0) { if (n <= 1) { Policies.ReportPoleError("Expint(n: {0}, x: {1}): Requires x > 0 when n <= 1", n, x); return(double.NaN); } return(1.0 / (n - 1)); } if (n == 0) { return(Math.Exp(-x) / x); } if (n == 1) { return(E1(x)); } if (n < 3) { if (x < 0.5) { return(_Expint.En_Series(n, x)); } return(_Expint.En_Fraction(n, x)); } if (x < (n - 2.0) / (n - 1.0)) { return(_Expint.En_Series(n, x)); } return(_Expint.En_Fraction(n, x)); }
/// <summary> /// Returns Γ(n) /// </summary> /// <param name="n">Argument</param> public static double Tgamma(int n) { if (n <= 0) { Policies.ReportPoleError("Tgamma(n: {0}): Requires n > 0", n); return(double.NaN); } if (n >= FactorialTable.Length + 1) { return(double.PositiveInfinity); } return(FactorialTable[n - 1]); }
/// <summary> /// Returns Tan(π * x) /// </summary> /// <param name="x">Argument</param> public static double TanPI(double x) { if (double.IsNaN(x) || double.IsInfinity(x)) { Policies.ReportDomainError("TanPI(x: {0}): Requires finite x", x); return(double.NaN); } // save x for error messages double originalX = x; // tan(-x) == -tan(x) bool neg = false; if (x < 0) { x = -x; neg = !neg; } // the period for tan(x) is π if (x >= 1) { x -= Math.Floor(x); } Debug.Assert(x >= 0 && x < 1); // shift x to [-0.5,0.5]*π if (x > 0.5) { x -= 1.0; } if (x == 0.5) { Policies.ReportPoleError("TanPI(x: {0}): Requires x not a half integer", originalX); return(double.NaN); } double result = Math.Tan(Math.PI * x); return((neg) ? -result : result); }
/// <summary> /// Returns Cot(π * x) /// </summary> /// <param name="x">Argument</param> public static double CotPI(double x) { if (double.IsNaN(x) || double.IsInfinity(x)) { Policies.ReportDomainError("CotPI(x: {0}): Requires finite x", x); return(double.NaN); } double absX = Math.Abs(x); // the period for cot(x) is π if (absX >= 1) { absX -= Math.Floor(absX); } if (absX == 0) { Policies.ReportPoleError("CotPI(x: {0}): Requires non-integer value for x", x); return(double.NaN); } Debug.Assert(absX > 0 && absX < 1); // cot(x) == tan(π/2 - x) double result; if (absX >= 0.5) { result = -Math.Tan((absX - 0.5) * Math.PI); } else { result = 1.0 / Math.Tan(absX * Math.PI); } // cot(-x) == -cot(x) return((x < 0) ? -result : result); }
/// <summary> /// Returns the Digamma function /// <para>ψ(x) = d/dx(ln(Γ(x))) = Γ'(x)/Γ(x)</para> /// </summary> /// <param name="x">Digamma function argument</param> public static double Digamma(double x) { if (double.IsNaN(x)) { Policies.ReportDomainError("Digamma(x: {0}): NaN not allowed", x); return(double.NaN); } if (double.IsInfinity(x)) { if (x < 0) { Policies.ReportDomainError("Digamma(x: {0}): Requires x != -Infinity", x); return(double.NaN); } return(double.PositiveInfinity); } if (x <= 0) { if (IsInteger(x)) { Policies.ReportPoleError("Digamma(x: {0}) Requires x is not an integer when x <= 0", x); return(double.NaN); } // use the following equations to reflect // ψ(1-x) - ψ(x) = π * cot(π*x) // ψ(-x) = 1/x + ψ(x) + π * cot(π*x) if (x < -1) { return(Digamma(1 - x) - Math.PI * Math2.CotPI(x)); } // The following is the forward recurrence: // -(1/x + 1/(x+1)) + Digamma(x + 2); // with a little less cancellation error near Digamma root: x = -0.50408... return(_Digamma.Rational_1_2(x + 2) - (2 * x + 1) / (x * (x + 1))); } // If we're above the lower-limit for the asymptotic expansion then use it: if (x >= 10) { const double p0 = 0.083333333333333333333333333333333333333333333333333; const double p1 = -0.0083333333333333333333333333333333333333333333333333; const double p2 = 0.003968253968253968253968253968253968253968253968254; const double p3 = -0.0041666666666666666666666666666666666666666666666667; const double p4 = 0.0075757575757575757575757575757575757575757575757576; const double p5 = -0.021092796092796092796092796092796092796092796092796; const double p6 = 0.083333333333333333333333333333333333333333333333333; const double p7 = -0.44325980392156862745098039215686274509803921568627; double xm1 = x - 1; double z = 1 / (xm1 * xm1); double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * (p5 + z * (p6 + z * p7)))))); return(Math.Log(xm1) + 1.0 / (2 * xm1) - z * P); } double result = 0; // // If x > 2 reduce to the interval [1,2]: // while (x > 2) { x -= 1; result += 1 / x; } // // If x < 1 use recurrence to shift to > 1: // if (x < 1) { result = -1 / x; x += 1; } result += _Digamma.Rational_1_2(x); return(result); }
/// <summary> /// Returns the Trigamma function /// <para>ψ'(x) = d/dx(ψ(x)) = d''/dx(ln(Γ(x)))</para> /// </summary> /// <param name="x">Trigamma function argument</param> public static double Trigamma(double x) { if (double.IsNaN(x)) { Policies.ReportDomainError("Trigamma(x: {0}): NaN not allowed", x); return(double.NaN); } if (double.IsInfinity(x)) { if (x < 0) { Policies.ReportDomainError("Trigamma(x: {0}): Requires x != -Infinity", x); return(double.NaN); } return(0); } double result = 0; // // Check for negative arguments and use reflection: // if (x <= 0) { if (IsInteger(x)) { Policies.ReportPoleError("Trigamma(x: {0}) Requires x is not an integer when x <= 0", x); return(double.NaN); } // Reflect: double z = 1 - x; double s = Math.Abs(x) < Math.Abs(z) ? Math2.SinPI(x) : Math2.SinPI(z); return(-Trigamma(z) + (Math.PI * Math.PI) / (s * s)); } if (x < 1) { result = 1 / (x * x); x += 1; } if (x <= 2) { // Max error in interpolated form: 3.736e-017 const double Y_1_2 = 2.1093254089355469; const double p0 = -1.1093280605946045; const double p1 = -3.8310674472619321; const double p2 = -3.3703848401898283; const double p3 = 0.28080574467981213; const double p4 = 1.6638069578676164; const double p5 = 0.64468386819102836; const double q0 = 1.0; const double q1 = 3.4535389668541151; const double q2 = 4.5208926987851437; const double q3 = 2.7012734178351534; const double q4 = 0.64468798399785611; const double q5 = -0.20314516859987728e-6; double z = x; double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * p5)))); double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * q5)))); result += (Y_1_2 + P / Q) / (x * x); } else if (x <= 4) { // Max error in interpolated form: 1.159e-017 const double p0 = -0.13803835004508849e-7; const double p1 = 0.50000049158540261; const double p2 = 1.6077979838469348; const double p3 = 2.5645435828098254; const double p4 = 2.0534873203680393; const double p5 = 0.74566981111565923; const double q0 = 1.0; const double q1 = 2.8822787662376169; const double q2 = 4.1681660554090917; const double q3 = 2.7853527819234466; const double q4 = 0.74967671848044792; const double q5 = -0.00057069112416246805; double z = 1 / x; double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * p5)))); double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * q5)))); result += (1 + P / Q) / x; } else { // Maximum Deviation Found: 6.896e-018 // Expected Error Term : -6.895e-018 // Maximum Relative Change in Control Points : 8.497e-004 const double p0 = 0.68947581948701249e-17; const double p1 = 0.49999999999998975; const double p2 = 1.0177274392923795; const double p3 = 2.498208511343429; const double p4 = 2.1921221359427595; const double p5 = 1.5897035272532764; const double p6 = 0.40154388356961734; const double q0 = 1.0; const double q1 = 1.7021215452463932; const double q2 = 4.4290431747556469; const double q3 = 2.9745631894384922; const double q4 = 2.3013614809773616; const double q5 = 0.28360399799075752; const double q6 = 0.022892987908906897; double z = 1 / x; double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * (p5 + z * p6))))); double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * (q5 + z * q6))))); result += (1 + P / Q) / x; } return(result); }
/// <summary> /// Returns Y{0}(x) /// </summary> /// <param name="x"></param> /// <returns></returns> public static double Y0(double x) { if (x < 0) { Policies.ReportDomainError("BesselY(v: 0, x: {0}): Requires x >= 0; complex number result not supported", x); return(double.NaN); } if (x == 0) { Policies.ReportPoleError("BesselY(v: 0, x: {0}): Overflow", x); return(double.NegativeInfinity); } if (double.IsInfinity(x)) { return(0); } double value, factor; if (x < DoubleLimits.MachineEpsilon) { return((2 / Math.PI) * (Math.Log(x / 2) + Constants.EulerMascheroni)); } // Bessel function of the second kind of order zero // x <= 8, minimax rational approximations on root-bracketing intervals // x > 8, Hankel asymptotic expansion in Hart, Computer Approximations, 1968 if (x <= 3) // x in [eps, 3] { const double Zero1 = 8.9357696627916752158e-01; const double Zero1a = 3837883847.0 / 4294967296; const double Zero1b = -8.66317862382940502854117587748531380699855129307710548e-11; const double p0 = 1.0723538782003176831e+11; const double p1 = -8.3716255451260504098e+09; const double p2 = 2.0422274357376619816e+08; const double p3 = -2.1287548474401797963e+06; const double p4 = 1.0102532948020907590e+04; const double p5 = -1.8402381979244993524e+01; const double q0 = 5.8873865738997033405e+11; const double q1 = 8.1617187777290363573e+09; const double q2 = 5.5662956624278251596e+07; const double q3 = 2.3889393209447253406e+05; const double q4 = 6.6475986689240190091e+02; const double q5 = 1.0; double z = x * x; double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * p5)))); double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * q5)))); double y = (2 / Math.PI) * Math.Log(x / Zero1) * _Bessel.J0(x); factor = (x + Zero1) * ((x - Zero1a) - Zero1b); value = y + factor * (P / Q); } else if (x <= 5.5) // x in (3, 5.5] { const double Zero2 = 3.9576784193148578684e+00; const double Zero2a = 16998099379.0 / 4294967296; const double Zero2b = 9.846238317388612698651281418603765563630625507511794841e-12; const double p0 = -2.2213976967566192242e+13; const double p1 = -5.5107435206722644429e+11; const double p2 = 4.3600098638603061642e+10; const double p3 = -6.9590439394619619534e+08; const double p4 = 4.6905288611678631510e+06; const double p5 = -1.4566865832663635920e+04; const double p6 = 1.7427031242901594547e+01; const double q0 = 4.3386146580707264428e+14; const double q1 = 5.4266824419412347550e+12; const double q2 = 3.4015103849971240096e+10; const double q3 = 1.3960202770986831075e+08; const double q4 = 4.0669982352539552018e+05; const double q5 = 8.3030857612070288823e+02; const double q6 = 1.0; double z = x * x; double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * (p5 + z * p6))))); double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * (q5 + z * q6))))); double y = (2 / Math.PI) * Math.Log(x / Zero2) * _Bessel.J0(x); factor = (x + Zero2) * ((x - Zero2a) - Zero2b); value = y + factor * (P / Q); } else if (x <= 8) // x in (5.5, 8] { const double Zero3 = 7.0860510603017726976e+00; const double Zero3a = 15217178781.0 / 2147483648; const double Zero3b = -5.07017534414388797421475310284896188222355301448323476e-11; const double p0 = -8.0728726905150210443e+15; const double p1 = 6.7016641869173237784e+14; const double p2 = -1.2829912364088687306e+11; const double p3 = -1.9363051266772083678e+11; const double p4 = 2.1958827170518100757e+09; const double p5 = -1.0085539923498211426e+07; const double p6 = 2.1363534169313901632e+04; const double p7 = -1.7439661319197499338e+01; const double q0 = 3.4563724628846457519e+17; const double q1 = 3.9272425569640309819e+15; const double q2 = 2.2598377924042897629e+13; const double q3 = 8.6926121104209825246e+10; const double q4 = 2.4727219475672302327e+08; const double q5 = 5.3924739209768057030e+05; const double q6 = 8.7903362168128450017e+02; const double q7 = 1.0; double z = x * x; double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * (p5 + z * (p6 + z * p7)))))); double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * (q5 + z * (q6 + z * q7)))))); double y = (2 / Math.PI) * Math.Log(x / Zero3) * _Bessel.J0(x); factor = (x + Zero3) * ((x - Zero3a) - Zero3b); value = y + factor * (P / Q); } else // x in (8, infinity) { double y = 8 / x; double z = y * y; double rc, rs; { const double p0 = 2.2779090197304684302e+04; const double p1 = 4.1345386639580765797e+04; const double p2 = 2.1170523380864944322e+04; const double p3 = 3.4806486443249270347e+03; const double p4 = 1.5376201909008354296e+02; const double p5 = 8.8961548424210455236e-01; const double q0 = 2.2779090197304684318e+04; const double q1 = 4.1370412495510416640e+04; const double q2 = 2.1215350561880115730e+04; const double q3 = 3.5028735138235608207e+03; const double q4 = 1.5711159858080893649e+02; const double q5 = 1.0; double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * p5)))); double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * q5)))); rc = P / Q; } { const double p0 = -8.9226600200800094098e+01; const double p1 = -1.8591953644342993800e+02; const double p2 = -1.1183429920482737611e+02; const double p3 = -2.2300261666214198472e+01; const double p4 = -1.2441026745835638459e+00; const double p5 = -8.8033303048680751817e-03; const double q0 = 5.7105024128512061905e+03; const double q1 = 1.1951131543434613647e+04; const double q2 = 7.2642780169211018836e+03; const double q3 = 1.4887231232283756582e+03; const double q4 = 9.0593769594993125859e+01; const double q5 = 1.0; double P = p0 + z * (p1 + z * (p2 + z * (p3 + z * (p4 + z * p5)))); double Q = q0 + z * (q1 + z * (q2 + z * (q3 + z * (q4 + z * q5)))); rs = P / Q; } // The following code is: //double z = x - 0.25f * Math.PI; //value = (Constants.RecipSqrtHalfPI / Math.Sqrt(x)) * (rc * Math.Sin(z) + y * rs * Math.Cos(z)); // Which can be written as: // (double, double) sincos = Math2.SinCos(x,-0.25); // value = (Constants.RecipSqrtHalfPI / Math.Sqrt(x)) * (rc * sincos.Item1 + y * rs * sincos.Item2); // Or, using trig addition rules, simplified to: value = (Constants.RecipSqrtPI / Math.Sqrt(x)) * ((rc + y * rs) * Math.Sin(x) + (-rc + y * rs) * Math.Cos(x)); } return(value); }
/// <summary> /// Returns the rising factorial: /// <para>(x)_{n} = Γ(x+n)/Γ(x)</para> /// </summary> /// <returns> /// <para> if n > 0, x*(x+1)*(x+2)...*(x+(n-1))</para> /// <para> if n == 0, 1</para> /// <para> if n < 0, 1/((x-1)*(x-2)*...*(x-n))</para> /// </returns> public static double FactorialRising(double x, int n) { // This section uses the following notation: // falling factorial: (x)_{_n} // rising factorial: (x)_{n} // Standard eqn for the rising factorial: // (x)_{n} = Γ(x+n)/Γ(x) double result = double.NaN; // (x)_{0} = 1 if (n == 0) { return(1); } if (double.IsNaN(x)) { Policies.ReportDomainError("FactorialRising(x: {0}, n: {1}): Requires x not NaN", x, n); return(double.NaN); } if (n == int.MinValue) { // avoid -int.MinValue error // this will overflow/underflow, but here it is if (x > 0 && IsInteger(x) && (x + n) <= 0) { Policies.ReportPoleError("FactorialRising(x: {0}, n: {1}): Divide by Zero", x, n); return(double.NaN); } // (x)_{n} = (x)_{n+1}/(x+n) return(FactorialRising(x, n + 1) / (x + n)); } if (x == 0) { // (0)_{n} = 0, n > 0 if (n > 0) { return(0); } // (0)_{-n} = (-1)^n / n! result = Math2.Factorial(-n); if (IsOdd(n)) { result = -result; } return(1 / result); } if (x < 0) { // For x less than zero, we really have a falling // factorial, modulo a possible change of sign. // Note: the falling factorial isn't defined for negative n // so cover that first if (n < -x) { // handle (n < 0 || ( n > 0 && n < -x)) directly // (x)_{n} = Γ(1-x)/Γ(1-x-n) result = Math2.TgammaDeltaRatio(1 - x, -n); } else { Debug.Assert(n > 0); result = FactorialFalling(-x, n); } if (IsOdd(n)) { result = -result; } return(result); } // x > 0 // standard case: (n > 0 || (n < 0 && x > -n)) // (x)_{n} = Γ(x+n)/Γ(x) if (-x < n) { return(1 / Math2.TgammaDeltaRatio(x, n)); } Debug.Assert((x + n) <= 0); // (x)_{n} = (-1)^n * Γ(1-x)/Γ(1-x-n) if (x < 1) { result = TgammaDeltaRatio(1 - x, -n); if (IsOdd(n)) { result = -result; } return(result); } // x >= 1 result = FactorialFalling(x - 1, -n); if (result == 0) { if (IsInteger(x)) { Policies.ReportPoleError("FactorialRising(x: {0}, n: {1}): Divide by Zero", x, n); return(double.NaN); } return(double.PositiveInfinity); } return(1 / result); }
/// <summary> /// Returns the natural log of the Gamma Function = ln(|Γ(x)|). /// Sets the sign = Sign(Γ(x)); 0 for poles or indeterminate. /// </summary> /// <param name="x">Lgamma function argument</param> /// <param name="sign">Lgamma output value = Sign(Γ(x))</param> public static double Lgamma(double x, out int sign) { sign = 0; if (double.IsNaN(x)) { Policies.ReportDomainError("Lgamma(x: {0}): NaN not allowed", x); return(double.NaN); } if (double.IsInfinity(x)) { if (x < 0) { Policies.ReportDomainError("Lgamma(x: {0}): Requires x != -Infinity", x); return(double.PositiveInfinity); } sign = 1; return(double.PositiveInfinity); } double result = 0; sign = 1; if (x <= 0) { if (IsInteger(x)) { sign = 0; Policies.ReportPoleError("Lgamma(x: {0}): Evaluation at zero, or a negative integer", x); return(double.PositiveInfinity); } if (x > -GammaSmall.UpperLimit) { return(GammaSmall.Lgamma(x, out sign)); } if (x <= -StirlingGamma.LowerLimit) { // Our Stirling routine does the reflection // So no need to reflect here result = StirlingGamma.Lgamma(x, out sign); return(result); } else { double product = 1; double xm2 = x - 2; double xm1 = x - 1; while (x < 1) { product *= x; xm2 = xm1; xm1 = x; x += 1; } if (product < 0) { sign = -1; product = -product; } result = -Math.Log(product) + _Lgamma.Rational_1_3(x, xm1, xm2); return(result); } } else if (x < 1) { if (x < GammaSmall.UpperLimit) { return(GammaSmall.Lgamma(x, out sign)); } // Log(Γ(x)) = Log(Γ(x+1)/x) result = -Math.Log(x) + _Lgamma.Rational_1_3(x + 1, x, x - 1); } else if (x < 3) { result = _Lgamma.Rational_1_3(x, x - 1, x - 2); } else if (x < 8) { // use recurrence to shift x to [2, 3) double product = 1; while (x >= 3) { product *= --x; } result = Math.Log(product) + _Lgamma.Rational_1_3(x, x - 1, x - 2); } else { // regular evaluation: result = StirlingGamma.Lgamma(x); } return(result); }
/// <summary> /// Returns the Gamma Function = Γ(x) /// </summary> /// <param name="x">Tgamma function argument</param> public static double Tgamma(double x) { if (double.IsNaN(x)) { Policies.ReportDomainError("Tgamma(x: {0}): NaN not allowed", x); return(double.NaN); } if (double.IsInfinity(x)) { if (x < 0) { Policies.ReportDomainError("Tgamma(x: {0}): Requires x is not negative infinity", x); return(double.NaN); } return(double.PositiveInfinity); } double result = 1; if (x <= 0) { if (IsInteger(x)) { Policies.ReportPoleError("Tgamma(x: {0}): Requires x != 0 and x be a non-negative integer", x); return(double.NaN); } if (x >= -GammaSmall.UpperLimit) { return(GammaSmall.Tgamma(x)); } if (x <= -StirlingGamma.LowerLimit) { const double MinX = -185; if (x < MinX) { return(0); } // If we use the reflection formula directly for x < -NegMaxGamma, Tgamma will overflow; // so, use recurrence to get x > -NegMaxGamma. // The result is likely to be subnormal or zero. double shiftedX = x; double recurrenceMult = 1.0; double NegMaxGamma = -(Math2.MaxFactorialIndex + 1); while (shiftedX < NegMaxGamma) { recurrenceMult *= shiftedX; shiftedX += 1; } result = -(Math.PI / StirlingGamma.Tgamma(-shiftedX)) / (shiftedX * recurrenceMult * Math2.SinPI(shiftedX)); return(result); } // shift x to > 0: double product = 1; while (x < 0) { product *= x++; } result /= product; // fall through } if (x < 1) { if (x <= GammaSmall.UpperLimit) { result *= GammaSmall.Tgamma(x); } else { // Γ(x) = Exp(LogΓ(x+1))/x result *= Math.Exp(_Lgamma.Rational_1_3(x + 1, x, x - 1)) / x; } } else if (IsInteger(x) && (x <= Math2.FactorialTable.Length)) { // Γ(n) = (n-1)! result *= Math2.FactorialTable[(int)x - 1]; } else if (x < StirlingGamma.LowerLimit) { // ensure x is in [1, 3) // if x >= 3, use recurrence to shift x to [2, 3) while (x >= 3) { result *= --x; } result *= Math.Exp(_Lgamma.Rational_1_3(x, x - 1, x - 2)); } else { result *= StirlingGamma.Tgamma(x); } return(result); }