source: sasmodels/sasmodels/models/lib/gamma.c @ 38daeec

core_shell_microgelscostrafo411magnetic_modelrelease_v0.94release_v0.95ticket-1257-vesicle-productticket_1156ticket_1265_superballticket_822_more_unit_tests
Last change on this file since 38daeec was 38daeec, checked in by wojciech, 8 years ago

Gamma functions added

  • Property mode set to 100644
File size: 8.1 KB
Line 
1/*                                                      gamma.c
2 *
3 *      Gamma function
4 *
5 *
6 *
7 * SYNOPSIS:
8 *
9 * double x, y, gamma();
10 * extern int sgngam;
11 *
12 * y = gamma( x );
13 *
14 *
15 *
16 * DESCRIPTION:
17 *
18 * Returns gamma function of the argument.  The result is
19 * correctly signed, and the sign (+1 or -1) is also
20 * returned in a global (extern) variable named sgngam.
21 * This variable is also filled in by the logarithmic gamma
22 * function lgam().
23 *
24 * Arguments |x| <= 34 are reduced by recurrence and the function
25 * approximated by a rational function of degree 6/7 in the
26 * interval (2,3).  Large arguments are handled by Stirling's
27 * formula. Large negative arguments are made positive using
28 * a reflection formula. 
29 *
30 *
31 * ACCURACY:
32 *
33 *                      Relative error:
34 * arithmetic   domain     # trials      peak         rms
35 *    DEC      -34, 34      10000       1.3e-16     2.5e-17
36 *    IEEE    -170,-33      20000       2.3e-15     3.3e-16
37 *    IEEE     -33,  33     20000       9.4e-16     2.2e-16
38 *    IEEE      33, 171.6   20000       2.3e-15     3.2e-16
39 *
40 * Error for arguments outside the test range will be larger
41 * owing to error amplification by the exponential function.
42 *
43 */
44/*                                                      lgam()
45 *
46 *      Natural logarithm of gamma function
47 *
48 *
49 *
50 * SYNOPSIS:
51 *
52 * double x, y, lgam();
53 * extern int sgngam;
54 *
55 * y = lgam( x );
56 *
57 *
58 *
59 * DESCRIPTION:
60 *
61 * Returns the base e (2.718...) logarithm of the absolute
62 * value of the gamma function of the argument.
63 * The sign (+1 or -1) of the gamma function is returned in a
64 * global (extern) variable named sgngam.
65 *
66 * For arguments greater than 13, the logarithm of the gamma
67 * function is approximated by the logarithmic version of
68 * Stirling's formula using a polynomial approximation of
69 * degree 4. Arguments between -33 and +33 are reduced by
70 * recurrence to the interval [2,3] of a rational approximation.
71 * The cosecant reflection formula is employed for arguments
72 * less than -33.
73 *
74 * Arguments greater than MAXLGM return MAXNUM and an error
75 * message.  MAXLGM = 2.035093e36 for DEC
76 * arithmetic or 2.556348e305 for IEEE arithmetic.
77 *
78 *
79 *
80 * ACCURACY:
81 *
82 *
83 * arithmetic      domain        # trials     peak         rms
84 *    DEC     0, 3                  7000     5.2e-17     1.3e-17
85 *    DEC     2.718, 2.035e36       5000     3.9e-17     9.9e-18
86 *    IEEE    0, 3                 28000     5.4e-16     1.1e-16
87 *    IEEE    2.718, 2.556e305     40000     3.5e-16     8.3e-17
88 * The error criterion was relative when the function magnitude
89 * was greater than one but absolute when it was less than one.
90 *
91 * The following test used the relative error criterion, though
92 * at certain points the relative error could be much higher than
93 * indicated.
94 *    IEEE    -200, -4             10000     4.8e-16     1.3e-16
95 *
96 */
97
98/*                                                      gamma.c */
99/*      gamma function  */
100
101/*
102Cephes Math Library Release 2.8:  June, 2000
103Copyright 1984, 1987, 1989, 1992, 2000 by Stephen L. Moshier
104*/
105
106
107double lanczos_gamma( double );
108
109double lanczos_gamma( double x) {
110
111#if FLOAT_SIZE > 4
112    double p, q, u, w, z;
113    int i;
114    int sgngam = 1;
115
116    const double LS2PI  =  0.91893853320467274178;
117    const double MAXLGM = 2.556348e305;
118    const double MAXNUM =  1.79769313486231570815E308;
119    const double LOGPI = 1.14472988584940017414;
120    const double PI = M_PI;
121
122    double A[8] = {
123        8.11614167470508450300E-4,
124        -5.95061904284301438324E-4,
125        7.93650340457716943945E-4,
126        -2.77777777730099687205E-3,
127        8.33333333333331927722E-2,
128        0.0,
129        0.0,
130        0.0
131    };
132    double B[8] = {
133        -1.37825152569120859100E3,
134        -3.88016315134637840924E4,
135        -3.31612992738871184744E5,
136        -1.16237097492762307383E6,
137        -1.72173700820839662146E6,
138        -8.53555664245765465627E5,
139        0.0,
140        0.0
141    };
142
143    double C[8] = {
144        /* 1.00000000000000000000E0, */
145        -3.51815701436523470549E2,
146        -1.70642106651881159223E4,
147        -2.20528590553854454839E5,
148        -1.13933444367982507207E6,
149        -2.53252307177582951285E6,
150        -2.01889141433532773231E6,
151        0.0,
152        0.0
153    };
154
155    sgngam = 1;
156
157    if( x < -34.0 ) {
158            q = -x;
159            w = lanczos_gamma(q); /* note this modifies sgngam! */
160            p = floor(q);
161            if( p == q ) {
162            lgsing:
163                        goto loverf;
164                }
165            i = p;
166            if( (i & 1) == 0 )
167                    sgngam = -1;
168            else
169                    sgngam = 1;
170            z = q - p;
171            if( z > 0.5 ) {
172                    p += 1.0;
173                    z = p - q;
174                }
175            z = q * sin( PI * z );
176            if( z == 0.0 )
177                    goto lgsing;
178            z = LOGPI - log( z ) - w;
179            return( z );
180        }
181
182    if( x < 13.0 ) {
183            z = 1.0;
184            p = 0.0;
185            u = x;
186            while( u >= 3.0 ) {
187                    p -= 1.0;
188                    u = x + p;
189                    z *= u;
190                }
191            while( u < 2.0 ) {
192                    if( u == 0.0 )
193                            goto lgsing;
194                    z /= u;
195                    p += 1.0;
196                    u = x + p;
197                }
198            if( z < 0.0 ) {
199                    sgngam = -1;
200                    z = -z;
201                }
202            else
203                    sgngam = 1;
204            if( u == 2.0 )
205                    return( log(z) );
206            p -= 2.0;
207            x = x + p;
208            p = x * polevl( x, B, 5 ) / p1evl( x, C, 6);
209            return( log(z) + p );
210        }
211
212    if( x > MAXLGM ) {
213        loverf:
214                return( sgngam * MAXNUM );
215        }
216
217    q = ( x - 0.5 ) * log(x) - x + LS2PI;
218    if( x > 1.0e8 )
219            return( q );
220
221    p = 1.0/(x*x);
222    if( x >= 1000.0 )
223            q += ((   7.9365079365079365079365e-4 * p
224                    - 2.7777777777777777777778e-3) *p
225                    + 0.0833333333333333333333) / x;
226    else
227            q += polevl( p, A, 4 ) / x;
228    return( q );
229#else
230
231    double p, q, w, z, xx;
232    double nx, tx;
233    int i, direction;
234    int sgngamf = 1;
235
236     double B[8] = {
237        6.055172732649237E-004,
238        -1.311620815545743E-003,
239        2.863437556468661E-003,
240        -7.366775108654962E-003,
241        2.058355474821512E-002,
242        -6.735323259371034E-002,
243        3.224669577325661E-001,
244        4.227843421859038E-001
245    };
246
247    /* log gamma(x+1), -.25 < x < .25 */
248    double C[8] = {
249        1.369488127325832E-001,
250        -1.590086327657347E-001,
251        1.692415923504637E-001,
252        -2.067882815621965E-001,
253        2.705806208275915E-001,
254        -4.006931650563372E-001,
255        8.224670749082976E-001,
256        -5.772156501719101E-001
257    };
258
259    /* log( sqrt( 2*pi ) ) */
260    const double LS2PI  =  0.91893853320467274178;
261    const double MAXLGM = 2.035093e36;
262    const double PIINV =  0.318309886183790671538;
263    const double MAXNUMF = 3.4028234663852885981170418348451692544e38;
264    const double PIF = 3.141592653589793238;
265    /* Logarithm of gamma function */
266
267    sgngamf = 1;
268
269    xx = x;
270    if( xx < 0.0 ) {
271            q = -xx;
272            w = lgamma(q); /* note this modifies sgngam! */
273            p = floor(q);
274            if( p == q )
275                    goto loverf;
276            i = p;
277
278            if( (i & 1) == 0 )
279                    sgngamf = -1;
280            else
281                    sgngamf = 1;
282
283            z = q - p;
284
285            if( z > 0.5 ) {
286                    p += 1.0;
287                    z = p - q;
288            }
289            z = q * sin( PIF * z );
290            if( z == 0.0 )
291                    goto loverf;
292            z = -log( PIINV*z ) - w;
293            return( z );
294        }
295
296    if( x < 6.5 ) {
297            direction = 0;
298            z = 1.0;
299            tx = x;
300            nx = 0.0;
301            if( x >= 1.5 ) {
302                    while( tx > 2.5 ) {
303                            nx -= 1.0;
304                            tx = x + nx;
305                            z *=tx;
306                        }
307                    x += nx - 2.0;
308            iv1r5:
309                    p = x * polevl( x, B, 7 );
310                    goto cont;
311        }
312        if( x >= 1.25 ) {
313                z *= x;
314                x -= 1.0; /* x + 1 - 2 */
315                direction = 1;
316                goto iv1r5;
317        }
318        if( x >= 0.75 ) {
319                x -= 1.0;
320                p = x * polevl( x, C, 7 );
321                q = 0.0;
322                goto contz;
323        }
324        while( tx < 1.5 ) {
325                if( tx == 0.0 )
326                        goto loverf;
327                z *=tx;
328                nx += 1.0;
329                tx = x + nx;
330        }
331        direction = 1;
332        x += nx - 2.0;
333        p = x * polevl( x, B, 7 );
334
335    cont:
336            if( z < 0.0 ) {
337                    sgngamf = -1;
338                    z = -z;
339        }
340            else {
341                    sgngamf = 1;
342                }
343            q = log(z);
344            if( direction )
345                    q = -q;
346    contz:
347            return( p + q );
348        }
349
350    if( x > MAXLGM ) {
351        loverf:
352            return( sgngamf * MAXNUMF );
353        }
354
355    /* Note, though an asymptotic formula could be used for x >= 3,
356    * there is cancellation error in the following if x < 6.5.  */
357    q = LS2PI - x;
358    q += ( x - 0.5 ) * log(x);
359
360    if( x <= 1.0e4 ) {
361            z = 1.0/x;
362            p = z * z;
363            q += ((    6.789774945028216E-004 * p
364                 - 2.769887652139868E-003 ) * p
365                +  8.333316229807355E-002 ) * z;
366        }
367    return( q );
368#endif
369}
Note: See TracBrowser for help on using the repository browser.