mas01mc@742
|
1 /*************************************
|
mas01mc@742
|
2 An ANSI-C implementation of the digamma-function for real arguments based
|
mas01mc@742
|
3 on the Chebyshev expansion proposed in appendix E of
|
mas01mc@742
|
4 http://arXiv.org/abs/math.CA/0403344 . For other implementations see
|
mas01mc@742
|
5 the GSL implementation for Psi(Digamma) in
|
mas01mc@742
|
6 http://www.gnu.org/software/gsl/manual/gsl-ref_toc.html
|
mas01mc@742
|
7
|
mas01mc@742
|
8 Richard J. Mathar, 2005-11-24
|
mas01mc@742
|
9 http://www.strw.leidenuniv.nl/~mathar/progs/digamma.c
|
mas01mc@742
|
10 **************************************/
|
mas01mc@742
|
11 #include <math.h>
|
mas01mc@742
|
12
|
mas01mc@742
|
13 #ifndef M_PIl
|
mas01mc@742
|
14 /** The constant Pi in high precision */
|
mas01mc@742
|
15 #define M_PIl 3.1415926535897932384626433832795029L
|
mas01mc@742
|
16 #endif
|
mas01mc@742
|
17 #ifndef M_GAMMAl
|
mas01mc@742
|
18 /** Euler's constant in high precision */
|
mas01mc@742
|
19 #define M_GAMMAl 0.5772156649015328606065120900824024L
|
mas01mc@742
|
20 #endif
|
mas01mc@742
|
21 #ifndef M_LN2l
|
mas01mc@742
|
22 /** the natural logarithm of 2 in high precision */
|
mas01mc@742
|
23 #define M_LN2l 0.6931471805599453094172321214581766L
|
mas01mc@742
|
24 #endif
|
mas01mc@742
|
25
|
mas01mc@742
|
26 /** The digamma function in long double precision.
|
mas01mc@742
|
27 * @param x the real value of the argument
|
mas01mc@742
|
28 * @return the value of the digamma (psi) function at that point
|
mas01mc@742
|
29 * @author Richard J. Mathar
|
mas01mc@742
|
30 * @since 2005-11-24
|
mas01mc@742
|
31 */
|
mas01mc@742
|
32 long double digammal(long double x)
|
mas01mc@742
|
33 {
|
mas01mc@742
|
34 /* force into the interval 1..3 */
|
mas01mc@742
|
35 if( x < 0.0L )
|
mas01mc@742
|
36 return digammal(1.0L-x)+M_PIl/tanl(M_PIl*(1.0L-x)) ; /* reflection formula */
|
mas01mc@742
|
37 else if( x < 1.0L )
|
mas01mc@742
|
38 return digammal(1.0L+x)-1.0L/x ;
|
mas01mc@742
|
39 else if ( x == 1.0L)
|
mas01mc@742
|
40 return -M_GAMMAl ;
|
mas01mc@742
|
41 else if ( x == 2.0L)
|
mas01mc@742
|
42 return 1.0L-M_GAMMAl ;
|
mas01mc@742
|
43 else if ( x == 3.0L)
|
mas01mc@742
|
44 return 1.5L-M_GAMMAl ;
|
mas01mc@742
|
45 else if ( x > 3.0L)
|
mas01mc@742
|
46 /* duplication formula */
|
mas01mc@742
|
47 return 0.5L*(digammal(x/2.0L)+digammal((x+1.0L)/2.0L))+M_LN2l ;
|
mas01mc@742
|
48 else
|
mas01mc@742
|
49 {
|
mas01mc@742
|
50 /* Just for your information, the following lines contain
|
mas01mc@742
|
51 * the Maple source code to re-generate the table that is
|
mas01mc@742
|
52 * eventually becoming the Kncoe[] array below
|
mas01mc@742
|
53 * interface(prettyprint=0) :
|
mas01mc@742
|
54 * Digits := 63 :
|
mas01mc@742
|
55 * r := 0 :
|
mas01mc@742
|
56 *
|
mas01mc@742
|
57 * for l from 1 to 60 do
|
mas01mc@742
|
58 * d := binomial(-1/2,l) :
|
mas01mc@742
|
59 * r := r+d*(-1)^l*(Zeta(2*l+1) -1) ;
|
mas01mc@742
|
60 * evalf(r) ;
|
mas01mc@742
|
61 * print(%,evalf(1+Psi(1)-r)) ;
|
mas01mc@742
|
62 *o d :
|
mas01mc@742
|
63 *
|
mas01mc@742
|
64 * for N from 1 to 28 do
|
mas01mc@742
|
65 * r := 0 :
|
mas01mc@742
|
66 * n := N-1 :
|
mas01mc@742
|
67 *
|
mas01mc@742
|
68 * for l from iquo(n+3,2) to 70 do
|
mas01mc@742
|
69 * d := 0 :
|
mas01mc@742
|
70 * for s from 0 to n+1 do
|
mas01mc@742
|
71 * d := d+(-1)^s*binomial(n+1,s)*binomial((s-1)/2,l) :
|
mas01mc@742
|
72 * od :
|
mas01mc@742
|
73 * if 2*l-n > 1 then
|
mas01mc@742
|
74 * r := r+d*(-1)^l*(Zeta(2*l-n) -1) :
|
mas01mc@742
|
75 * fi :
|
mas01mc@742
|
76 * od :
|
mas01mc@742
|
77 * print(evalf((-1)^n*2*r)) ;
|
mas01mc@742
|
78 *od :
|
mas01mc@742
|
79 *quit :
|
mas01mc@742
|
80 */
|
mas01mc@742
|
81 static long double Kncoe[] = { .30459198558715155634315638246624251L,
|
mas01mc@742
|
82 .72037977439182833573548891941219706L, -.12454959243861367729528855995001087L,
|
mas01mc@742
|
83 .27769457331927827002810119567456810e-1L, -.67762371439822456447373550186163070e-2L,
|
mas01mc@742
|
84 .17238755142247705209823876688592170e-2L, -.44817699064252933515310345718960928e-3L,
|
mas01mc@742
|
85 .11793660000155572716272710617753373e-3L, -.31253894280980134452125172274246963e-4L,
|
mas01mc@742
|
86 .83173997012173283398932708991137488e-5L, -.22191427643780045431149221890172210e-5L,
|
mas01mc@742
|
87 .59302266729329346291029599913617915e-6L, -.15863051191470655433559920279603632e-6L,
|
mas01mc@742
|
88 .42459203983193603241777510648681429e-7L, -.11369129616951114238848106591780146e-7L,
|
mas01mc@742
|
89 .304502217295931698401459168423403510e-8L, -.81568455080753152802915013641723686e-9L,
|
mas01mc@742
|
90 .21852324749975455125936715817306383e-9L, -.58546491441689515680751900276454407e-10L,
|
mas01mc@742
|
91 .15686348450871204869813586459513648e-10L, -.42029496273143231373796179302482033e-11L,
|
mas01mc@742
|
92 .11261435719264907097227520956710754e-11L, -.30174353636860279765375177200637590e-12L,
|
mas01mc@742
|
93 .80850955256389526647406571868193768e-13L, -.21663779809421233144009565199997351e-13L,
|
mas01mc@742
|
94 .58047634271339391495076374966835526e-14L, -.15553767189204733561108869588173845e-14L,
|
mas01mc@742
|
95 .41676108598040807753707828039353330e-15L, -.11167065064221317094734023242188463e-15L } ;
|
mas01mc@742
|
96
|
mas01mc@742
|
97 register long double Tn_1 = 1.0L ; /* T_{n-1}(x), started at n=1 */
|
mas01mc@742
|
98 register long double Tn = x-2.0L ; /* T_{n}(x) , started at n=1 */
|
mas01mc@742
|
99 register long double resul = Kncoe[0] + Kncoe[1]*Tn ;
|
mas01mc@742
|
100 register int n;
|
mas01mc@742
|
101
|
mas01mc@742
|
102 x -= 2.0L ;
|
mas01mc@742
|
103
|
mas01mc@742
|
104 for(n = 2 ; n < sizeof(Kncoe)/sizeof(long double) ;n++)
|
mas01mc@742
|
105 {
|
mas01mc@742
|
106 const long double Tn1 = 2.0L * x * Tn - Tn_1 ; /* Chebyshev recursion, Eq. 22.7.4 Abramowitz-Stegun */
|
mas01mc@742
|
107 resul += Kncoe[n]*Tn1 ;
|
mas01mc@742
|
108 Tn_1 = Tn ;
|
mas01mc@742
|
109 Tn = Tn1 ;
|
mas01mc@742
|
110 }
|
mas01mc@742
|
111 return resul ;
|
mas01mc@742
|
112 }
|
mas01mc@742
|
113 }
|
mas01mc@742
|
114
|
mas01mc@742
|
115 /** The optional interface to CREASO's IDL is added if someone has defined
|
mas01mc@742
|
116 * the cpp macro export_IDL_REF, which typically has been done by including the
|
mas01mc@742
|
117 * files stdio.h and idl_export.h before this one here.
|
mas01mc@742
|
118 */
|
mas01mc@742
|
119 #ifdef export_IDL_REF
|
mas01mc@742
|
120 /** CALL_EXTERNAL interface.
|
mas01mc@742
|
121 * dg = CALL_EXTERNAL('digamma.so',X)
|
mas01mc@742
|
122 * @pararm argc the number of arguments. This is supposed to be 1 and not
|
mas01mc@742
|
123 * checked further because it might have negative impact on performance.
|
mas01mc@742
|
124 * @pararm argv the parameter list. The first element is the parameter x
|
mas01mc@742
|
125 * supposed to be of type DOUBLE in IDL
|
mas01mc@742
|
126 * @return the return value, again of IDL-type DOUBLE
|
mas01mc@742
|
127 * @since 2007-01-16
|
mas01mc@742
|
128 * @author Richard J. Mathar
|
mas01mc@742
|
129 */
|
mas01mc@742
|
130 double digammal_idl(int argc, void *argv[])
|
mas01mc@742
|
131 {
|
mas01mc@742
|
132 long double x = *(double*)argv[0] ;
|
mas01mc@742
|
133 return (double)digammal(x) ;
|
mas01mc@742
|
134 }
|
mas01mc@742
|
135 #endif /* export_IDL_REF */
|
mas01mc@742
|
136
|
mas01mc@742
|
137
|
mas01mc@742
|
138 #ifdef MAIN
|
mas01mc@742
|
139
|
mas01mc@742
|
140 #include <stdlib.h>
|
mas01mc@742
|
141 #include <stdio.h>
|
mas01mc@742
|
142
|
mas01mc@742
|
143 int main(int argc, char **argv){
|
mas01mc@742
|
144 if (argc < 2){
|
mas01mc@742
|
145 fprintf(stderr, "Syntax: %s x\n", argv[0]);
|
mas01mc@742
|
146 exit(1);
|
mas01mc@742
|
147 }
|
mas01mc@742
|
148 double x = atof(argv[1]);
|
mas01mc@742
|
149 double y = digammal(x);
|
mas01mc@742
|
150 printf("%g\n", y);
|
mas01mc@742
|
151 return 0;
|
mas01mc@742
|
152 }
|
mas01mc@742
|
153 #endif /* MAIN */
|
mas01mc@742
|
154
|
mas01mc@742
|
155 #ifdef TEST
|
mas01mc@742
|
156
|
mas01mc@742
|
157 /* an alternate implementation for test purposes, using formula 6.3.16 of Abramowitz/Stegun with the
|
mas01mc@742
|
158 first n terms */
|
mas01mc@742
|
159 #include <stdio.h>
|
mas01mc@742
|
160
|
mas01mc@742
|
161 long double digammalAlt(long double x, int n)
|
mas01mc@742
|
162 {
|
mas01mc@742
|
163 /* force into the interval 1..3 */
|
mas01mc@742
|
164 if( x < 0.0L )
|
mas01mc@742
|
165 return digammalAlt(1.0L-x,n)+M_PIl/tanl(M_PIl*(1.0L-x)) ; /* reflection formula */
|
mas01mc@742
|
166 else if( x < 1.0L )
|
mas01mc@742
|
167 return digammalAlt(1.0L+x,n)-1.0L/x ;
|
mas01mc@742
|
168 else if ( x == 1.0L)
|
mas01mc@742
|
169 return -M_GAMMAl ;
|
mas01mc@742
|
170 else if ( x == 2.0L)
|
mas01mc@742
|
171 return 1.0L-M_GAMMAl ;
|
mas01mc@742
|
172 else if ( x == 3.0L)
|
mas01mc@742
|
173 return 1.5L-M_GAMMAl ;
|
mas01mc@742
|
174 else if ( x > 3.0L)
|
mas01mc@742
|
175 return digammalAlt(x-1.0L,n)+1.0L/(x-1.0L) ;
|
mas01mc@742
|
176 else
|
mas01mc@742
|
177 {
|
mas01mc@742
|
178 x -= 1.0L ;
|
mas01mc@742
|
179 register long double resul = -M_GAMMAl ;
|
mas01mc@742
|
180
|
mas01mc@742
|
181 for( ; n >= 1 ;n--)
|
mas01mc@742
|
182 resul += x/(n*(n+x)) ;
|
mas01mc@742
|
183 return resul ;
|
mas01mc@742
|
184 }
|
mas01mc@742
|
185 }
|
mas01mc@742
|
186 int main(int argc, char *argv[])
|
mas01mc@742
|
187 {
|
mas01mc@742
|
188 long double x;
|
mas01mc@742
|
189 for(x=0.01 ; x < 5. ; x += 0.02)
|
mas01mc@742
|
190 printf("%.2Lf %.30Lf %.30Lf %.30Lf\n",x, digammal(x), digammalAlt(x,100), digammalAlt(x,200) ) ;
|
mas01mc@742
|
191 }
|
mas01mc@742
|
192
|
mas01mc@742
|
193 #endif /* TEST */
|