1 | /* erf.c |
---|
2 | reference - Haruhiko Okumura: C-gengo niyoru saishin algorithm jiten |
---|
3 | (New Algorithm handbook in C language) (Gijyutsu hyouron |
---|
4 | sha, Tokyo, 1991) p.227 [in Japanese] */ |
---|
5 | #include <stdio.h> |
---|
6 | #include <math.h> |
---|
7 | |
---|
8 | #ifdef _MSC_VER |
---|
9 | |
---|
10 | |
---|
11 | #ifdef _WIN32 |
---|
12 | # include <float.h> |
---|
13 | # if !defined __MINGW32__ || defined __NO_ISOCEXT |
---|
14 | # ifndef isnan |
---|
15 | # define isnan(x) _isnan(x) |
---|
16 | # endif |
---|
17 | # ifndef isinf |
---|
18 | # define isinf(x) (!_finite(x) && !_isnan(x)) |
---|
19 | # endif |
---|
20 | # ifndef finite |
---|
21 | # define finite(x) _finite(x) |
---|
22 | # endif |
---|
23 | # endif |
---|
24 | #endif |
---|
25 | |
---|
26 | static double q_gamma(double, double, double); |
---|
27 | |
---|
28 | /* Incomplete gamma function |
---|
29 | 1 / Gamma(a) * Int_0^x exp(-t) t^(a-1) dt */ |
---|
30 | static double p_gamma(a, x, loggamma_a) |
---|
31 | double a, x, loggamma_a; |
---|
32 | { |
---|
33 | int k; |
---|
34 | double result, term, previous; |
---|
35 | |
---|
36 | if (x >= 1 + a) return 1 - q_gamma(a, x, loggamma_a); |
---|
37 | if (x == 0) return 0; |
---|
38 | result = term = exp(a * log(x) - x - loggamma_a) / a; |
---|
39 | for (k = 1; k < 1000; k++) { |
---|
40 | term *= x / (a + k); |
---|
41 | previous = result; result += term; |
---|
42 | if (result == previous) return result; |
---|
43 | } |
---|
44 | fprintf(stderr, "erf.c:%d:p_gamma() could not converge.", __LINE__); |
---|
45 | return result; |
---|
46 | } |
---|
47 | |
---|
48 | /* Incomplete gamma function |
---|
49 | 1 / Gamma(a) * Int_x^inf exp(-t) t^(a-1) dt */ |
---|
50 | static double q_gamma(a, x, loggamma_a) |
---|
51 | double a, x, loggamma_a; |
---|
52 | { |
---|
53 | int k; |
---|
54 | double result, w, temp, previous; |
---|
55 | double la = 1, lb = 1 + x - a; /* Laguerre polynomial */ |
---|
56 | |
---|
57 | if (x < 1 + a) return 1 - p_gamma(a, x, loggamma_a); |
---|
58 | w = exp(a * log(x) - x - loggamma_a); |
---|
59 | result = w / lb; |
---|
60 | for (k = 2; k < 1000; k++) { |
---|
61 | temp = ((k - 1 - a) * (lb - la) + (k + x) * lb) / k; |
---|
62 | la = lb; lb = temp; |
---|
63 | w *= (k - 1 - a) / k; |
---|
64 | temp = w / (la * lb); |
---|
65 | previous = result; result += temp; |
---|
66 | if (result == previous) return result; |
---|
67 | } |
---|
68 | fprintf(stderr, "erf.c:%d:q_gamma() could not converge.", __LINE__); |
---|
69 | return result; |
---|
70 | } |
---|
71 | |
---|
72 | #define LOG_PI_OVER_2 0.572364942924700087071713675675 /* log_e(PI)/2 */ |
---|
73 | |
---|
74 | double erf(x) |
---|
75 | double x; |
---|
76 | { |
---|
77 | if (!finite(x)) { |
---|
78 | if (isnan(x)) return x; /* erf(NaN) = NaN */ |
---|
79 | return (x>0 ? 1.0 : -1.0); /* erf(+-inf) = +-1.0 */ |
---|
80 | } |
---|
81 | if (x >= 0) return p_gamma(0.5, x * x, LOG_PI_OVER_2); |
---|
82 | else return - p_gamma(0.5, x * x, LOG_PI_OVER_2); |
---|
83 | } |
---|
84 | |
---|
85 | double erfc(x) |
---|
86 | double x; |
---|
87 | { |
---|
88 | if (!finite(x)) { |
---|
89 | if (isnan(x)) return x; /* erfc(NaN) = NaN */ |
---|
90 | return (x>0 ? 0.0 : 2.0); /* erfc(+-inf) = 0.0, 2.0 */ |
---|
91 | } |
---|
92 | if (x >= 0) return q_gamma(0.5, x * x, LOG_PI_OVER_2); |
---|
93 | else return 1 + p_gamma(0.5, x * x, LOG_PI_OVER_2); |
---|
94 | } |
---|
95 | |
---|
96 | #endif |
---|