| 1 |
/** |
|---|
| 2 |
* Implementation of lgamma() and tgamma() for those C systems that are missing it. |
|---|
| 3 |
* |
|---|
| 4 |
Macros: |
|---|
| 5 |
* TABLE_SV = <table border=1 cellpadding=4 cellspacing=0> |
|---|
| 6 |
* <caption>Special Values</caption> |
|---|
| 7 |
* $0</table> |
|---|
| 8 |
* SVH = $(TR $(TH $1) $(TH $2)) |
|---|
| 9 |
* SV = $(TR $(TD $1) $(TD $2)) |
|---|
| 10 |
* GAMMA = Γ |
|---|
| 11 |
* INTEGRATE = $(BIG ∫<sub>$(SMALL $1)</sub><sup>$2</sup>) |
|---|
| 12 |
* POWER = $1<sup>$2</sup> |
|---|
| 13 |
* NAN = $(RED NAN) |
|---|
| 14 |
*/ |
|---|
| 15 |
module mathextra.etcgamma; |
|---|
| 16 |
/* Cephes code Copyright 1994 by Stephen L. Moshier |
|---|
| 17 |
* Converted to D and enhanced by Don Clugston; changes placed into the public domain. |
|---|
| 18 |
*/ |
|---|
| 19 |
private import std.math; |
|---|
| 20 |
debug import std.stdio; |
|---|
| 21 |
|
|---|
| 22 |
unittest { |
|---|
| 23 |
debug writefln("--- UnitTest: " __FILE__ " ---"); |
|---|
| 24 |
} |
|---|
| 25 |
|
|---|
| 26 |
//------------------------------------------------------------------ |
|---|
| 27 |
private { |
|---|
| 28 |
|
|---|
| 29 |
const real SQRT2PI = 2.50662827463100050242E0L; // sqrt(2pi) |
|---|
| 30 |
// exp(gamma(x)) == inf if x>MAXGAMMA |
|---|
| 31 |
const real MAXGAMMA = 1755.455L; |
|---|
| 32 |
|
|---|
| 33 |
// Polynomial approximations for gamma and loggamma. |
|---|
| 34 |
|
|---|
| 35 |
const real GammaNumeratorCoeffs[] = [ |
|---|
| 36 |
0x1p+0, // 1 |
|---|
| 37 |
0x1.acf42d903366539ep-1, // 0.83780043015731267283 |
|---|
| 38 |
0x1.73a991c8475f1aeap-2, // 0.36295154366402391688 |
|---|
| 39 |
0x1.c7e918751d6b2a92p-4, // 0.1113062816019361559 |
|---|
| 40 |
0x1.86d162cca32cfe86p-6, // 0.023853632434611082525 |
|---|
| 41 |
0x1.0c378e2e6eaf7cd8p-8, // 0.0040926668283940355009 |
|---|
| 42 |
0x1.dc5c66b7d05feb54p-12, // 0.00045429319606080091555 |
|---|
| 43 |
0x1.616457b47e448694p-15 // 4.2127604874716220134e-05 |
|---|
| 44 |
]; |
|---|
| 45 |
|
|---|
| 46 |
const real GammaDenominatorCoeffs[] = [ |
|---|
| 47 |
0x1p+0, // 1 |
|---|
| 48 |
0x1.a8f9faae5d8fc8bp-2, // 0.41501609505884554346 |
|---|
| 49 |
-0x1.cb7895a6756eebdep-3, // -0.22435109056703291645 |
|---|
| 50 |
-0x1.7b9bab006d30652ap-5, // -0.046338876712445342138 |
|---|
| 51 |
0x1.c671af78f312082ep-6, // 0.027737065658400729792 |
|---|
| 52 |
-0x1.a11ebbfaf96252dcp-11, // -0.00079559336824947383209 |
|---|
| 53 |
-0x1.447b4d2230a77ddap-10, // -0.0012377992466531522311 |
|---|
| 54 |
0x1.ec1d45bb85e06696p-13, // 0.00023465840591606352443 |
|---|
| 55 |
-0x1.d4ce24d05bd0a8e6p-17 // -1.3971485174761704409e-05 |
|---|
| 56 |
]; |
|---|
| 57 |
|
|---|
| 58 |
const real SmallStirlingCoeffs[] = [ |
|---|
| 59 |
0x1.55555555555543aap-4, // 0.083333333333333318004 |
|---|
| 60 |
0x1.c71c71c720dd8792p-9, // 0.0034722222222300753277 |
|---|
| 61 |
-0x1.5f7268f0b5907438p-9, // -0.0026813271618763044182 |
|---|
| 62 |
-0x1.e13cd410e0477de6p-13, // -0.00022947197478731854057 |
|---|
| 63 |
0x1.9b0f31643442616ep-11, // 0.00078403348427447530038 |
|---|
| 64 |
0x1.2527623a3472ae08p-14, // 6.9893322606231931717e-05 |
|---|
| 65 |
-0x1.37f6bc8ef8b374dep-11, // -0.00059502375540563301557 |
|---|
| 66 |
-0x1.8c968886052b872ap-16, // -2.3638488095017590616e-05 |
|---|
| 67 |
0x1.76baa9c6d3eeddbcp-11 // 0.0007147391378143610789 |
|---|
| 68 |
]; |
|---|
| 69 |
|
|---|
| 70 |
const real LargeStirlingCoeffs[] = [ |
|---|
| 71 |
1.0L, |
|---|
| 72 |
8.33333333333333333333E-2L, |
|---|
| 73 |
3.47222222222222222222E-3L, |
|---|
| 74 |
-2.68132716049382716049E-3L, |
|---|
| 75 |
-2.29472093621399176955E-4L, |
|---|
| 76 |
7.84039221720066627474E-4L, |
|---|
| 77 |
6.97281375836585777429E-5L |
|---|
| 78 |
]; |
|---|
| 79 |
|
|---|
| 80 |
const real GammaSmallCoeffs[] = [ |
|---|
| 81 |
0x1p+0, // 1 |
|---|
| 82 |
0x1.2788cfc6fb618f52p-1, // 0.57721566490153286082 |
|---|
| 83 |
-0x1.4fcf4026afa2f7ecp-1, // -0.65587807152025406846 |
|---|
| 84 |
-0x1.5815e8fa24d7e306p-5, // -0.042002635034033440541 |
|---|
| 85 |
0x1.5512320aea2ad71ap-3, // 0.16653861137208052067 |
|---|
| 86 |
-0x1.59af0fb9d82e216p-5, // -0.042197733607059154702 |
|---|
| 87 |
-0x1.3b4b61d3bfdf244ap-7, // -0.0096220233604062716456 |
|---|
| 88 |
0x1.d9358e9d9d69fd34p-8, // 0.0072205994780369096722 |
|---|
| 89 |
-0x1.38fc4bcbada775d6p-10 // -0.0011939450513815100956 |
|---|
| 90 |
]; |
|---|
| 91 |
|
|---|
| 92 |
const real GammaSmallNegCoeffs[] = [ |
|---|
| 93 |
-0x1p+0, // -1 |
|---|
| 94 |
0x1.2788cfc6fb618f54p-1, // 0.57721566490153286086 |
|---|
| 95 |
0x1.4fcf4026afa2bc4cp-1, // 0.65587807152025365473 |
|---|
| 96 |
-0x1.5815e8fa2468fec8p-5, // -0.042002635034021129105 |
|---|
| 97 |
-0x1.5512320baedaf4b6p-3, // -0.16653861139444135193 |
|---|
| 98 |
-0x1.59af0fa283baf07ep-5, // -0.042197733437311917216 |
|---|
| 99 |
0x1.3b4a70de31e05942p-7, // 0.0096219111550359767339 |
|---|
| 100 |
0x1.d9398be3bad13136p-8, // 0.0072208372618931703258 |
|---|
| 101 |
0x1.291b73ee05bcbba2p-10 // 0.001133374167243894382 |
|---|
| 102 |
]; |
|---|
| 103 |
|
|---|
| 104 |
const real logGammaStirlingCoeffs[] = [ |
|---|
| 105 |
0x1.5555555555553f98p-4, // 0.083333333333333314473 |
|---|
| 106 |
-0x1.6c16c16c07509b1p-9, // -0.0027777777777503496034 |
|---|
| 107 |
0x1.a01a012461cbf1e4p-11, // 0.00079365077958550707556 |
|---|
| 108 |
-0x1.3813089d3f9d164p-11, // -0.00059523458517656885149 |
|---|
| 109 |
0x1.b911a92555a277b8p-11, // 0.00084127232973224980805 |
|---|
| 110 |
-0x1.ed0a7b4206087b22p-10, // -0.0018808019381193769072 |
|---|
| 111 |
0x1.402523859811b308p-8 // 0.0048850261424322707812 |
|---|
| 112 |
]; |
|---|
| 113 |
|
|---|
| 114 |
const real logGammaNumerator[] = [ |
|---|
| 115 |
-0x1.0edd25913aaa40a2p+23, // -8875666.7836507038022 |
|---|
| 116 |
-0x1.31c6ce2e58842d1ep+24, // -20039374.181038151756 |
|---|
| 117 |
-0x1.f015814039477c3p+23, // -16255680.62543700591 |
|---|
| 118 |
-0x1.74ffe40c4b184b34p+22, // -6111225.0120052143001 |
|---|
| 119 |
-0x1.0d9c6d08f9eab55p+20, // -1104326.8146914642612 |
|---|
| 120 |
-0x1.54c6b71935f1fc88p+16, // -87238.715228435114593 |
|---|
| 121 |
-0x1.0e761b42932b2aaep+11 // -2163.6908276438128575 |
|---|
| 122 |
]; |
|---|
| 123 |
|
|---|
| 124 |
const real logGammaDenominator[] = [ |
|---|
| 125 |
-0x1.4055572d75d08c56p+24, // -20993367.177578958762 |
|---|
| 126 |
-0x1.deeb6013998e4d76p+24, // -31386464.076561826621 |
|---|
| 127 |
-0x1.106f7cded5dcc79ep+24, // -17854332.870450781569 |
|---|
| 128 |
-0x1.25e17184848c66d2p+22, // -4814940.3794118821866 |
|---|
| 129 |
-0x1.301303b99a614a0ap+19, // -622744.11640662195015 |
|---|
| 130 |
-0x1.09e76ab41ae965p+15, // -34035.708405343046707 |
|---|
| 131 |
-0x1.00f95ced9e5f54eep+9, // -513.94814844353701437 |
|---|
| 132 |
0x1p+0 // 1 |
|---|
| 133 |
]; |
|---|
| 134 |
|
|---|
| 135 |
/* |
|---|
| 136 |
* Helper function: Gamma function computed by Stirling's formula. |
|---|
| 137 |
* |
|---|
| 138 |
* Stirling's formula for the gamma function is: |
|---|
| 139 |
* |
|---|
| 140 |
* $(GAMMA)(x) = sqrt(2 π) x<sup>x-0.5</sup> exp(-x) (1 + 1/x P(1/x)) |
|---|
| 141 |
* |
|---|
| 142 |
*/ |
|---|
| 143 |
real gammaStirling(real x) |
|---|
| 144 |
{ |
|---|
| 145 |
// CEPHES code Copyright 1994 by Stephen L. Moshier |
|---|
| 146 |
|
|---|
| 147 |
real w = 1.0L/x; |
|---|
| 148 |
real y = exp(x); |
|---|
| 149 |
if ( x > 1024.0L ) { |
|---|
| 150 |
// For large x, use rational coefficients from the analytical expansion. |
|---|
| 151 |
w = poly(w, LargeStirlingCoeffs); |
|---|
| 152 |
// Avoid overflow in pow() |
|---|
| 153 |
real v = pow( x, 0.5L * x - 0.25L ); |
|---|
| 154 |
y = v * (v / y); |
|---|
| 155 |
} |
|---|
| 156 |
else { |
|---|
| 157 |
w = 1.0L + w * poly( w, SmallStirlingCoeffs); |
|---|
| 158 |
y = pow( x, x - 0.5L ) / y; |
|---|
| 159 |
} |
|---|
| 160 |
y = SQRT2PI * y * w; |
|---|
| 161 |
return y; |
|---|
| 162 |
} |
|---|
| 163 |
|
|---|
| 164 |
} // private |
|---|
| 165 |
|
|---|
| 166 |
/**************** |
|---|
| 167 |
* The sign of $(GAMMA)(x). |
|---|
| 168 |
* |
|---|
| 169 |
* Returns -1 if $(GAMMA)(x) < 0, +1 if $(GAMMA)(x) > 0, |
|---|
| 170 |
* $(NAN) if sign is indeterminate. |
|---|
| 171 |
*/ |
|---|
| 172 |
real sgnGamma(real x) |
|---|
| 173 |
{ |
|---|
| 174 |
/* Author: Don Clugston. License: Public domain. */ |
|---|
| 175 |
if (x > 0) return 1.0; |
|---|
| 176 |
if (x < -1/real.epsilon) { |
|---|
| 177 |
// Large negatives lose all precision |
|---|
| 178 |
return real.nan; |
|---|
| 179 |
} |
|---|
| 180 |
// if (remquo(x, -1.0, n) == 0) { |
|---|
| 181 |
int n = cast(int)(x); |
|---|
| 182 |
if (x == n) { |
|---|
| 183 |
return x == 0 ? copysign(1, x) : real.nan; |
|---|
| 184 |
} |
|---|
| 185 |
return n & 1 ? 1.0 : -1.0; |
|---|
| 186 |
} |
|---|
| 187 |
|
|---|
| 188 |
unittest { |
|---|
| 189 |
assert(sgnGamma(5.0) == 1.0); |
|---|
| 190 |
assert(isnan(sgnGamma(-3.0))); |
|---|
| 191 |
assert(sgnGamma(-0.1) == -1.0); |
|---|
| 192 |
assert(sgnGamma(-55.1) == 1.0); |
|---|
| 193 |
assert(isnan(sgnGamma(-real.infinity))); |
|---|
| 194 |
} |
|---|
| 195 |
|
|---|
| 196 |
/***************************************************** |
|---|
| 197 |
* The Gamma function, $(GAMMA)(x) |
|---|
| 198 |
* |
|---|
| 199 |
* $(GAMMA)(x) is a generalisation of the factorial function |
|---|
| 200 |
* to real and complex numbers. |
|---|
| 201 |
* Like x!, $(GAMMA)(x+1) = x*$(GAMMA)(x). |
|---|
| 202 |
* |
|---|
| 203 |
* Mathematically, if z.re > 0 then |
|---|
| 204 |
* $(GAMMA)(z) = $(INTEGRATE 0, ∞) $(POWER t, z-1)$(POWER e, -t) dt |
|---|
| 205 |
* |
|---|
| 206 |
* $(TABLE_SV |
|---|
| 207 |
* $(SVH x, $(GAMMA)(x) ) |
|---|
| 208 |
* $(SV $(NAN), $(NAN) ) |
|---|
| 209 |
* $(SV ±0.0, ±∞) |
|---|
| 210 |
* $(SV integer > 0, (x-1)! ) |
|---|
| 211 |
* $(SV integer < 0, $(NAN) ) |
|---|
| 212 |
* $(SV +∞, +∞ ) |
|---|
| 213 |
* $(SV -∞, $(NAN) ) |
|---|
| 214 |
* ) |
|---|
| 215 |
*/ |
|---|
| 216 |
real tgamma(real x) |
|---|
| 217 |
{ |
|---|
| 218 |
/* Author: Don Clugston. Based on code from the CEPHES library. |
|---|
| 219 |
* CEPHES code Copyright 1994 by Stephen L. Moshier |
|---|
| 220 |
* |
|---|
| 221 |
* Arguments |x| <= 13 are reduced by recurrence and the function |
|---|
| 222 |
* approximated by a rational function of degree 7/8 in the |
|---|
| 223 |
* interval (2,3). Large arguments are handled by Stirling's |
|---|
| 224 |
* formula. Large negative arguments are made positive using |
|---|
| 225 |
* a reflection formula. |
|---|
| 226 |
*/ |
|---|
| 227 |
|
|---|
| 228 |
real q, z; |
|---|
| 229 |
if (isnan(x)) return x; |
|---|
| 230 |
if (x == -x.infinity) return real.nan; |
|---|
| 231 |
if ( fabs(x) > MAXGAMMA ) return real.infinity; |
|---|
| 232 |
if (x==0) return 1.0/x; // +- infinity depending on sign of x, create an exception. |
|---|
| 233 |
|
|---|
| 234 |
q = fabs(x); |
|---|
| 235 |
|
|---|
| 236 |
if ( q > 13.0L ) { |
|---|
| 237 |
// Large arguments are handled by Stirling's |
|---|
| 238 |
// formula. Large negative arguments are made positive using |
|---|
| 239 |
// the reflection formula. |
|---|
| 240 |
|
|---|
| 241 |
if ( x < 0.0L ) { |
|---|
| 242 |
int sgngam = 1; // sign of gamma. |
|---|
| 243 |
real p = floor(q); |
|---|
| 244 |
if (p == q) |
|---|
| 245 |
return real.nan; // poles for all integers <0. |
|---|
| 246 |
int intpart = cast(int)(p); |
|---|
| 247 |
if ( (intpart & 1) == 0 ) |
|---|
| 248 |
sgngam = -1; |
|---|
| 249 |
z = q - p; |
|---|
| 250 |
if ( z > 0.5L ) { |
|---|
| 251 |
p += 1.0L; |
|---|
| 252 |
z = q - p; |
|---|
| 253 |
} |
|---|
| 254 |
z = q * sin( PI * z ); |
|---|
| 255 |
z = fabs(z) * gammaStirling(q); |
|---|
| 256 |
if ( z <= PI/real.max ) return sgngam * real.infinity; |
|---|
| 257 |
return sgngam * PI/z; |
|---|
| 258 |
} else { |
|---|
| 259 |
return gammaStirling(x); |
|---|
| 260 |
} |
|---|
| 261 |
} |
|---|
| 262 |
|
|---|
| 263 |
// Arguments |x| <= 13 are reduced by recurrence and the function |
|---|
| 264 |
// approximated by a rational function of degree 7/8 in the |
|---|
| 265 |
// interval (2,3). |
|---|
| 266 |
|
|---|
| 267 |
z = 1.0L; |
|---|
| 268 |
while ( x >= 3.0L ) { |
|---|
| 269 |
x -= 1.0L; |
|---|
| 270 |
z *= x; |
|---|
| 271 |
} |
|---|
| 272 |
|
|---|
| 273 |
while ( x < -0.03125L ) { |
|---|
| 274 |
z /= x; |
|---|
| 275 |
x += 1.0L; |
|---|
| 276 |
} |
|---|
| 277 |
|
|---|
| 278 |
if ( x <= 0.03125L ) { |
|---|
| 279 |
if ( x == 0.0L ) |
|---|
| 280 |
return real.nan; |
|---|
| 281 |
else { |
|---|
| 282 |
if ( x < 0.0L ) { |
|---|
| 283 |
x = -x; |
|---|
| 284 |
return z / (x * poly( x, GammaSmallNegCoeffs )); |
|---|
| 285 |
} else { |
|---|
| 286 |
return z / (x * poly( x, GammaSmallCoeffs )); |
|---|
| 287 |
} |
|---|
| 288 |
} |
|---|
| 289 |
} |
|---|
| 290 |
|
|---|
| 291 |
while ( x < 2.0L ) { |
|---|
| 292 |
z /= x; |
|---|
| 293 |
x += 1.0L; |
|---|
| 294 |
} |
|---|
| 295 |
if ( x == 2.0L ) return z; |
|---|
| 296 |
|
|---|
| 297 |
x -= 2.0L; |
|---|
| 298 |
return z * poly( x, GammaNumeratorCoeffs ) / poly( x, GammaDenominatorCoeffs ); |
|---|
| 299 |
} |
|---|
| 300 |
|
|---|
| 301 |
unittest { |
|---|
| 302 |
// gamma(n) = factorial(n-1) if n is an integer. |
|---|
| 303 |
real fact = 1.0L; |
|---|
| 304 |
for (int i=1; fact<real.max; ++i) { |
|---|
| 305 |
// Require exact equality for small factorials |
|---|
| 306 |
if (i<14) assert(tgamma(i*1.0L) == fact); |
|---|
| 307 |
assert(feqrel(tgamma(i*1.0L), fact) > real.mant_dig-15); |
|---|
| 308 |
fact *= (i*1.0L); |
|---|
| 309 |
} |
|---|
| 310 |
assert(tgamma(0.0) == real.infinity); |
|---|
| 311 |
assert(tgamma(-0.0) == -real.infinity); |
|---|
| 312 |
assert(isnan(tgamma(-1.0))); |
|---|
| 313 |
assert(isnan(tgamma(-15.0))); |
|---|
| 314 |
assert(isnan(tgamma(real.nan))); |
|---|
| 315 |
assert(tgamma(real.infinity) == real.infinity); |
|---|
| 316 |
assert(tgamma(real.max) == real.infinity); |
|---|
| 317 |
assert(isnan(tgamma(-real.infinity))); |
|---|
| 318 |
assert(tgamma(real.min*real.epsilon) == real.infinity); |
|---|
| 319 |
|
|---|
| 320 |
// Test some high-precision values (50 decimal digits) |
|---|
| 321 |
const real SQRT_PI = 1.77245385090551602729816748334114518279754945612238L; |
|---|
| 322 |
|
|---|
| 323 |
assert(feqrel(tgamma(0.5L), SQRT_PI) == real.mant_dig); |
|---|
| 324 |
|
|---|
| 325 |
assert(feqrel(tgamma(1.0/3.L), 2.67893853470774763365569294097467764412868937795730L) >= real.mant_dig-2); |
|---|
| 326 |
assert(feqrel(tgamma(0.25L), |
|---|
| 327 |
3.62560990822190831193068515586767200299516768288006) >= real.mant_dig-1); |
|---|
| 328 |
assert(feqrel(tgamma(1.0/5.0L), |
|---|
| 329 |
4.59084371199880305320475827592915200343410999829340L) >= real.mant_dig-1); |
|---|
| 330 |
} |
|---|
| 331 |
|
|---|
| 332 |
/***************************************************** |
|---|
| 333 |
* Natural logarithm of gamma function. |
|---|
| 334 |
* |
|---|
| 335 |
* Returns the base e (2.718...) logarithm of the absolute |
|---|
| 336 |
* value of the gamma function of the argument. |
|---|
| 337 |
* |
|---|
| 338 |
* For reals, lgamma is equivalent to log(fabs(gamma(x))). |
|---|
| 339 |
* |
|---|
| 340 |
* $(TABLE_SV |
|---|
| 341 |
* $(SVH x, lgamma(x) ) |
|---|
| 342 |
* $(SV $(NAN), $(NAN) ) |
|---|
| 343 |
* $(SV integer <= 0, +∞ ) |
|---|
| 344 |
* $(SV ±∞, +∞ ) |
|---|
| 345 |
* ) |
|---|
| 346 |
*/ |
|---|
| 347 |
real lgamma(real x) |
|---|
| 348 |
{ |
|---|
| 349 |
/* Author: Don Clugston. Based on code from the CEPHES library. |
|---|
| 350 |
* CEPHES code Copyright 1994 by Stephen L. Moshier |
|---|
| 351 |
* |
|---|
| 352 |
* For arguments greater than 33, the logarithm of the gamma |
|---|
| 353 |
* function is approximated by the logarithmic version of |
|---|
| 354 |
* Stirling's formula using a polynomial approximation of |
|---|
| 355 |
* degree 4. Arguments between -33 and +33 are reduced by |
|---|
| 356 |
* recurrence to the interval [2,3] of a rational approximation. |
|---|
| 357 |
* The cosecant reflection formula is employed for arguments |
|---|
| 358 |
* less than -33. |
|---|
| 359 |
*/ |
|---|
| 360 |
real q, w, z, f, nx; |
|---|
| 361 |
|
|---|
| 362 |
if (isnan(x)) return x; |
|---|
| 363 |
if (fabs(x) == x.infinity) return x.infinity; |
|---|
| 364 |
|
|---|
| 365 |
if( x < -34.0L ) { |
|---|
| 366 |
q = -x; |
|---|
| 367 |
w = lgamma(q); |
|---|
| 368 |
real p = floor(q); |
|---|
| 369 |
if ( p == q ) return real.infinity; |
|---|
| 370 |
int intpart = cast(int)(p); |
|---|
| 371 |
real sgngam = 1; |
|---|
| 372 |
if ( (intpart & 1) == 0 ) |
|---|
| 373 |
sgngam = -1; |
|---|
| 374 |
z = q - p; |
|---|
| 375 |
if ( z > 0.5L ) { |
|---|
| 376 |
p += 1.0L; |
|---|
| 377 |
z = p - q; |
|---|
| 378 |
} |
|---|
| 379 |
z = q * sin( PI * z ); |
|---|
| 380 |
if ( z == 0.0L ) return sgngam * real.infinity; |
|---|
| 381 |
/* z = LOGPI - logl( z ) - w; */ |
|---|
| 382 |
z = log( PI/z ) - w; |
|---|
| 383 |
return z; |
|---|
| 384 |
} |
|---|
| 385 |
|
|---|
| 386 |
if( x < 13.0L ) { |
|---|
| 387 |
z = 1.0L; |
|---|
| 388 |
nx = floor( x + 0.5L ); |
|---|
| 389 |
f = x - nx; |
|---|
| 390 |
while ( x >= 3.0L ) { |
|---|
| 391 |
nx -= 1.0L; |
|---|
| 392 |
x = nx + f; |
|---|
| 393 |
z *= x; |
|---|
| 394 |
} |
|---|
| 395 |
while ( x < 2.0L ) { |
|---|
| 396 |
if( fabs(x) <= 0.03125 ) { |
|---|
| 397 |
if ( x == 0.0L ) return real.infinity; |
|---|
| 398 |
if ( x < 0.0L ) { |
|---|
| 399 |
x = -x; |
|---|
| 400 |
q = z / (x * poly( x, GammaSmallNegCoeffs)); |
|---|
| 401 |
} else |
|---|
| 402 |
q = z / (x * poly( x, GammaSmallCoeffs)); |
|---|
| 403 |
return log( fabs(q) ); |
|---|
| 404 |
} |
|---|
| 405 |
z /= nx + f; |
|---|
| 406 |
nx += 1.0L; |
|---|
| 407 |
x = nx + f; |
|---|
| 408 |
} |
|---|
| 409 |
z = fabs(z); |
|---|
| 410 |
if ( x == 2.0L ) |
|---|
| 411 |
return log(z); |
|---|
| 412 |
x = (nx - 2.0L) + f; |
|---|
| 413 |
real p = x * poly( x, logGammaNumerator ) / poly( x, logGammaDenominator); |
|---|
| 414 |
return log(z) + p; |
|---|
| 415 |
} |
|---|
| 416 |
|
|---|
| 417 |
// const real MAXLGM = 1.04848146839019521116e+4928L; |
|---|
| 418 |
// if( x > MAXLGM ) return sgngaml * real.infinity; |
|---|
| 419 |
|
|---|
| 420 |
const real LOGSQRT2PI = 0.91893853320467274178L; // log( sqrt( 2*pi ) ) |
|---|
| 421 |
|
|---|
| 422 |
q = ( x - 0.5L ) * log(x) - x + LOGSQRT2PI; |
|---|
| 423 |
if (x > 1.0e10L) return q; |
|---|
| 424 |
real p = 1.0L / (x*x); |
|---|
| 425 |
q += poly( p, logGammaStirlingCoeffs ) / x; |
|---|
| 426 |
return q ; |
|---|
| 427 |
} |
|---|
| 428 |
|
|---|
| 429 |
unittest { |
|---|
| 430 |
assert(isnan(lgamma(real.nan))); |
|---|
| 431 |
assert(lgamma(real.infinity) == real.infinity); |
|---|
| 432 |
assert(lgamma(-1.0) == real.infinity); |
|---|
| 433 |
assert(lgamma(0.0) == real.infinity); |
|---|
| 434 |
assert(lgamma(-50.0) == real.infinity); |
|---|
| 435 |
assert(isPosZero(lgamma(1.0L))); |
|---|
| 436 |
assert(isPosZero(lgamma(2.0L))); |
|---|
| 437 |
assert(lgamma(real.min*real.epsilon) == real.infinity); |
|---|
| 438 |
assert(lgamma(-real.min*real.epsilon) == real.infinity); |
|---|
| 439 |
|
|---|
| 440 |
// x, correct loggamma(x), correct d/dx loggamma(x). |
|---|
| 441 |
static real[] testpoints = [ |
|---|
| 442 |
8.0L, 8.525146484375L + 1.48766904143001655310E-5, 2.01564147795560999654E0L, |
|---|
| 443 |
8.99993896484375e-1L, 6.6375732421875e-2L + 5.11505711292524166220E-6L, -7.54938684259372234258E-1, |
|---|
| 444 |
7.31597900390625e-1L, 2.2369384765625e-1 + 5.21506341809849792422E-6L, -1.13355566660398608343E0L, |
|---|
| 445 |
2.31639862060546875e-1L, 1.3686676025390625L + 1.12609441752996145670E-5L, -4.56670961813812679012E0, |
|---|
| 446 |
1.73162841796875L, -8.88214111328125e-2L + 3.36207740803753034508E-6L, 2.33339034686200586920E-1L, |
|---|
| 447 |
1.23162841796875L, -9.3902587890625e-2L + 1.28765089229009648104E-5L, -2.49677345775751390414E-1L, |
|---|
| 448 |
7.3786976294838206464e19L, 3.301798506038663053312e21L - 1.656137564136932662487046269677E5L, |
|---|
| 449 |
4.57477139169563904215E1L, |
|---|
| 450 |
1.08420217248550443401E-19L, 4.36682586669921875e1L + 1.37082843669932230418E-5L, |
|---|
| 451 |
-9.22337203685477580858E18L, |
|---|
| 452 |
1.0L, 0.0L, -5.77215664901532860607E-1L, |
|---|
| 453 |
2.0L, 0.0L, 4.22784335098467139393E-1L, |
|---|
| 454 |
-0.5L, 1.2655029296875L + 9.19379714539648894580E-6L, 3.64899739785765205590E-2L, |
|---|
| 455 |
-1.5L, 8.6004638671875e-1L + 6.28657731014510932682E-7L, 7.03156640645243187226E-1L, |
|---|
| 456 |
-2.5L, -5.6243896484375E-2L + 1.79986700949327405470E-7, 1.10315664064524318723E0L, |
|---|
| 457 |
-3.5L, -1.30902099609375L + 1.43111007079536392848E-5L, 1.38887092635952890151E0L |
|---|
| 458 |
]; |
|---|
| 459 |
// TODO: test derivatives as well. |
|---|
| 460 |
for (int i=0; i<testpoints.length; i+=3) { |
|---|
| 461 |
assert( feqrel(lgamma(testpoints[i]), testpoints[i+1]) > real.mant_dig-5); |
|---|
| 462 |
if (testpoints[i]<MAXGAMMA) { |
|---|
| 463 |
assert( feqrel(log(fabs(tgamma(testpoints[i]))), testpoints[i+1]) > real.mant_dig-5); |
|---|
| 464 |
} |
|---|
| 465 |
} |
|---|
| 466 |
assert(lgamma(-50.2) == log(fabs(tgamma(-50.2)))); |
|---|
| 467 |
assert(lgamma(-0.008) == log(fabs(tgamma(-0.008)))); |
|---|
| 468 |
assert(feqrel(lgamma(-38.8),log(fabs(tgamma(-38.8)))) > real.mant_dig-4); |
|---|
| 469 |
assert(feqrel(lgamma(1500.0L),log(tgamma(1500.0L))) > real.mant_dig-2); |
|---|
| 470 |
} |
|---|