source: sasview/sansmodels/src/sans/models/c_models/gamma_win.cpp @ 83bf68e1

ESS_GUIESS_GUI_DocsESS_GUI_batch_fittingESS_GUI_bumps_abstractionESS_GUI_iss1116ESS_GUI_iss879ESS_GUI_iss959ESS_GUI_openclESS_GUI_orderingESS_GUI_sync_sascalccostrafo411magnetic_scattrelease-4.1.1release-4.1.2release-4.2.2release_4.0.1ticket-1009ticket-1094-headlessticket-1242-2d-resolutionticket-1243ticket-1249ticket885unittest-saveload
Last change on this file since 83bf68e1 was 83bf68e1, checked in by Mathieu Doucet <doucetm@…>, 12 years ago

Re #5 fixing samsmodels compilation on MSVC

  • Property mode set to 100644
File size: 4.7 KB
Line 
1// Visit http://www.johndcook.com/stand_alone_code.html for the source of this code and more like it.
2
3#include <cmath>
4#include <sstream>
5#include <iostream>
6#include <stdexcept>
7
8#include "gamma_win.h"
9
10// Note that the functions Gamma and LogGamma are mutually dependent.
11
12double gamma
13(
14    double x    // We require x > 0
15)
16{
17  if (x <= 0.0)
18  {
19    std::stringstream os;
20        os << "Invalid input argument " << x <<  ". Argument must be positive.";
21        throw std::invalid_argument( os.str() );
22  }
23
24    // Split the function domain into three intervals:
25    // (0, 0.001), [0.001, 12), and (12, infinity)
26
27    ///////////////////////////////////////////////////////////////////////////
28    // First interval: (0, 0.001)
29  //
30  // For small x, 1/Gamma(x) has power series x + gamma x^2  - ...
31  // So in this range, 1/Gamma(x) = x + gamma x^2 with error on the order of x^3.
32  // The relative error over this interval is less than 6e-7.
33
34  const double gamma = 0.577215664901532860606512090; // Euler's gamma constant
35
36    if (x < 0.001)
37        return 1.0/(x*(1.0 + gamma*x));
38
39    ///////////////////////////////////////////////////////////////////////////
40    // Second interval: [0.001, 12)
41
42  if (x < 12.0)
43    {
44        // The algorithm directly approximates gamma over (1,2) and uses
45        // reduction identities to reduce other arguments to this interval.
46
47    double y = x;
48        int n = 0;
49        bool arg_was_less_than_one = (y < 1.0);
50
51        // Add or subtract integers as necessary to bring y into (1,2)
52        // Will correct for this below
53        if (arg_was_less_than_one)
54        {
55            y += 1.0;
56        }
57        else
58        {
59            n = static_cast<int> (floor(y)) - 1;  // will use n later
60            y -= n;
61        }
62
63        // numerator coefficients for approximation over the interval (1,2)
64        static const double p[] =
65        {
66            -1.71618513886549492533811E+0,
67             2.47656508055759199108314E+1,
68            -3.79804256470945635097577E+2,
69             6.29331155312818442661052E+2,
70             8.66966202790413211295064E+2,
71            -3.14512729688483675254357E+4,
72            -3.61444134186911729807069E+4,
73             6.64561438202405440627855E+4
74        };
75
76        // denominator coefficients for approximation over the interval (1,2)
77        static const double q[] =
78        {
79            -3.08402300119738975254353E+1,
80             3.15350626979604161529144E+2,
81            -1.01515636749021914166146E+3,
82            -3.10777167157231109440444E+3,
83             2.25381184209801510330112E+4,
84             4.75584627752788110767815E+3,
85            -1.34659959864969306392456E+5,
86            -1.15132259675553483497211E+5
87        };
88
89        double num = 0.0;
90        double den = 1.0;
91        int i;
92
93        double z = y - 1;
94        for (i = 0; i < 8; i++)
95        {
96            num = (num + p[i])*z;
97            den = den*z + q[i];
98        }
99        double result = num/den + 1.0;
100
101        // Apply correction if argument was not initially in (1,2)
102        if (arg_was_less_than_one)
103        {
104            // Use identity gamma(z) = gamma(z+1)/z
105            // The variable "result" now holds gamma of the original y + 1
106            // Thus we use y-1 to get back the orginal y.
107            result /= (y-1.0);
108        }
109        else
110        {
111            // Use the identity gamma(z+n) = z*(z+1)* ... *(z+n-1)*gamma(z)
112            for (i = 0; i < n; i++)
113                result *= y++;
114        }
115
116    return result;
117    }
118
119    ///////////////////////////////////////////////////////////////////////////
120    // Third interval: [12, infinity)
121
122    if (x > 171.624)
123    {
124    // Correct answer too large to display. Force +infinity.
125    double temp = DBL_MAX;
126    return temp*2.0;
127    }
128
129    return exp(LogGamma(x));
130}
131
132double lgamma
133(
134    double x    // x must be positive
135)
136{
137  if (x <= 0.0)
138  {
139    std::stringstream os;
140        os << "Invalid input argument " << x <<  ". Argument must be positive.";
141        throw std::invalid_argument( os.str() );
142  }
143
144    if (x < 12.0)
145    {
146        return log(fabs(Gamma(x)));
147    }
148
149  // Abramowitz and Stegun 6.1.41
150    // Asymptotic series should be good to at least 11 or 12 figures
151    // For error analysis, see Whittiker and Watson
152    // A Course in Modern Analysis (1927), page 252
153
154    static const double c[8] =
155    {
156     1.0/12.0,
157    -1.0/360.0,
158    1.0/1260.0,
159    -1.0/1680.0,
160    1.0/1188.0,
161    -691.0/360360.0,
162    1.0/156.0,
163    -3617.0/122400.0
164    };
165    double z = 1.0/(x*x);
166    double sum = c[7];
167    for (int i=6; i >= 0; i--)
168    {
169        sum *= z;
170        sum += c[i];
171    }
172    double series = sum/x;
173
174    static const double halfLogTwoPi = 0.91893853320467274178032973640562;
175    double logGamma = (x - 0.5)*log(x) - x + halfLogTwoPi + series;
176  return logGamma;
177}
Note: See TracBrowser for help on using the repository browser.