LMMC
Lamina Mathematic core
载入中...
搜索中...
未找到
special_functions.c
浏览该文件的文档.
1#include <math.h>
3#include <float.h>
5
8 if (!out) { return LMMC_STATUS_INVALID_ARGUMENT; }
9 *out = erf(x);
10 return LMMC_STATUS_OK;
11}
12
19 if (!out) { return LMMC_STATUS_INVALID_ARGUMENT; }
20 *out = erfc(x);
21 return LMMC_STATUS_OK;
22}
23
31 if (!out) { return LMMC_STATUS_INVALID_ARGUMENT; }
32 if (x <= 0.0) { return LMMC_STATUS_INVALID_ARGUMENT; }
33
35 static const double coeff[9] = {
36 0.99999999999980993,
37 676.5203681218851,
38 -1259.1392167224028,
39 771.32342877765313,
40 -176.61502916214059,
41 12.507343278686905,
42 -0.13857109526572012,
43 9.9843695780195716e-6,
44 1.5056327351493116e-7
45 };
46
47 double g = 7.0;
48 double tmp, ser;
49 int i;
50
51 if (x < 0.5) {
54 double sinpx = sin(LMMC_PI * x);
55 if (fabs(sinpx) < 1e-300) { return LMMC_STATUS_NUMERICAL_FAILURE; }
56 lmmc_real_t lg1mx;
57 lmmc_status_t st = lmmc_lgamma(1.0 - x, &lg1mx);
58 if (st != LMMC_STATUS_OK) { return st; }
59 *out = log(LMMC_PI) - log(fabs(sinpx)) - lg1mx;
60 return LMMC_STATUS_OK;
61 }
62
63 x -= 1.0;
64 tmp = x + g + 0.5;
65 ser = coeff[0];
66 for (i = 1; i < 9; i++) {
67 ser += coeff[i] / (x + (double)i);
68 }
69
70 *out = 0.5 * log(2.0 * LMMC_PI) + (x + 0.5) * log(tmp) - tmp + log(ser);
71 return LMMC_STATUS_OK;
72}
73
76 if (!out) { return LMMC_STATUS_INVALID_ARGUMENT; }
77 if (x <= 0.0) { return LMMC_STATUS_INVALID_ARGUMENT; }
78
79 lmmc_real_t lg;
80 lmmc_status_t st = lmmc_lgamma(x, &lg);
81 if (st != LMMC_STATUS_OK) { return st; }
82
83 if (lg > 709.0) {
85 }
86
87 *out = exp(lg);
88 return LMMC_STATUS_OK;
89}
90
93 double logarithm, value;
94 lmmc_status_t status;
95 if (!out) { return LMMC_STATUS_INVALID_ARGUMENT; }
96 status = lmmc_log_beta(a, b, &logarithm);
97 if (status != LMMC_STATUS_OK) { return status; }
98 if (logarithm > log(DBL_MAX)) { return LMMC_STATUS_NUMERICAL_FAILURE; }
99 value = exp(logarithm);
100 if (!isfinite(value)) { return LMMC_STATUS_NUMERICAL_FAILURE; }
101 *out = value;
102 return LMMC_STATUS_OK;
103}
104
113 if (!out) { return LMMC_STATUS_INVALID_ARGUMENT; }
114 if (x <= 0.0) { return LMMC_STATUS_INVALID_ARGUMENT; }
115
116 double result = 0.0;
117
118 while (x < 7.0) {
119 result -= 1.0 / x;
120 x += 1.0;
121 }
122
125 {
126 double ix = 1.0 / x;
127 double ix2 = ix * ix;
128
130 static const double bernoulli_coeff[] = {
131 1.0 / 12.0,
132 -1.0 / 120.0,
133 1.0 / 252.0,
134 -1.0 / 240.0,
135 5.0 / 660.0,
136 -691.0 / 32760.0,
137 1.0 / 12.0
138 };
139
140 double sum = 0.0;
141 double ix2k = ix2;
142 int k;
143 for (k = 0; k < 7; k++) {
144 sum -= bernoulli_coeff[k] * ix2k;
145 ix2k *= ix2;
146 }
147
148 result += log(x) - 0.5 * ix + sum;
149 }
150
151 *out = result;
152 return LMMC_STATUS_OK;
153}
154
159static double lambertw_ulp(double value) {
160 const double magnitude = fabs(value);
161 return nextafter(magnitude, INFINITY) - magnitude;
162}
163
164static void lambertw_residual(
165 double w, double z, int region, double target, double target_error,
166 double* residual, double* radius, double* first, double* second) {
167 const double unit = DBL_EPSILON / 2.0;
168 if (region == 1) {
173 static const double coefficient[] = {
174 0.5, 0.33333333333333331, 0.125, 0.033333333333333333,
175 0.0069444444444444441, 0.0011904761904761906,
176 0.00017361111111111112, 2.2045855379188714e-05,
177 2.4801587301587302e-06, 2.5052108385441718e-07,
178 2.296443268665491e-08, 1.9270852604185937e-09,
179 1.4911969277048643e-10, 1.0706029224547743e-11,
180 7.1692159985810778e-13, 4.498331606952833e-14,
181 2.6552651846596585e-15, 1.4797143443923793e-16,
182 7.8096034842931133e-18, 3.9145882126782523e-19,
183 1.8683261924146203e-20, 8.5099743753875053e-22,
184 3.7069964135210721e-23, 1.5472680682522736e-24,
185 6.1989906580619936e-26, 2.3877593645868422e-27,
186 8.855700940088562e-29, 3.1667896082053609e-30,
187 1.0932964123566126e-31, 3.6483751246605535e-33,
188 1.1781211340049704e-34
189 };
190 double p, absolute, square, value, exponential;
191 int k;
192 p = coefficient[30];
193 absolute = fabs(p);
194 for (k = 29; k >= 0; --k) {
195 p = fma(p, w, coefficient[k]);
196 absolute = fma(absolute, fabs(w), coefficient[k]);
197 }
198 square = w * w;
199 value = square * p;
200 *residual = value - target;
201 *radius = (96.0 * unit / (1.0 - 96.0 * unit)) *
202 square * absolute + target_error +
203 unit * fabs(*residual) +
204 4.0e-36 * pow(fabs(w), 33.0);
205 exponential = exp(w);
206 *first = w * exponential;
207 *second = (1.0 + w) * exponential;
208 } else if (region == 2) {
209 const double logarithm = log(fabs(w));
210 double low;
211 const double sum = lmmc_two_sum(w, logarithm, &low);
212 *residual = (sum - target) + low;
213 *radius = 2.0 * lambertw_ulp(logarithm) + target_error +
214 4.0 * unit * (fabs(sum - target) + fabs(low)) +
215 2.0 * unit * fabs(sum);
216 *first = 1.0 + 1.0 / w;
217 *second = -(1.0 / w) / w;
218 } else {
219 const double em1 = expm1(w);
220 const double difference = w - z;
221 *residual = fma(w, em1, difference);
222 *radius = 2.0 * fabs(w) * lambertw_ulp(em1) +
223 unit * (fabs(difference) + fabs(*residual)) +
224 4.0 * nextafter(0.0, 1.0);
225 *first = exp(w) * (1.0 + w);
226 *second = exp(w) * (2.0 + w);
227 }
228}
229
230typedef struct {
234
235static void lambertw_initialize(double z, int branch, lmmc_lambertw_state_t* state) {
236 state->target = 0.0;
237 state->target_error = 0.0;
238 state->step = INFINITY;
239 if (z < -0.3) {
240 const double e_hi = 2.718281828459045;
241 const double e_lo = 1.4456468917292502e-16;
242 const double high = fma(e_hi, z, 1.0);
243 const double low = e_lo * z;
244 double remainder;
245 state->target = lmmc_two_sum(high, low, &remainder);
246 state->target_error = fabs(remainder) +
247 DBL_EPSILON * (fabs(high) + fabs(low)) + 2.0e-32 * fabs(z);
248 state->region = 1;
249 state->increasing = branch == 0;
250 state->lo = state->increasing ? 0.0 : -1.0;
251 state->hi = state->increasing ? 1.0 : 0.0;
252 state->w = (state->increasing ? 1.0 : -1.0) * sqrt(2.0 * state->target) -
253 (2.0 / 3.0) * state->target;
254 } else if (branch == -1 || z > 1.0) {
255 state->region = 2;
256 state->increasing = 1;
257 state->target = log(fabs(z));
258 state->target_error = 2.0 * lambertw_ulp(state->target);
259 if (branch == -1) {
260 state->lo = 2.0 * state->target;
261 state->hi = -1.0;
262 state->w = state->target - log(-state->target);
263 } else {
264 state->lo = 0.5;
265 state->hi = state->target + 1.0;
266 state->w = z <= 2.718281828459045 ? 1.0 : state->target - log(state->target);
267 }
268 } else {
269 state->region = 0;
270 state->increasing = 1;
271 state->lo = z < 0.0 ? -1.0 : 0.0;
272 state->hi = z < 0.0 ? 0.0 : z;
273 state->w = z < 0.0 ? z : z / (1.0 + z);
274 }
275 if (!(state->w > state->lo && state->w < state->hi)) {
276 state->w = state->lo + (state->hi - state->lo) / 2.0;
277 }
278}
279
280static int lambertw_accept_enclosure(
281 double z, const lmmc_lambertw_state_t* state, double tolerance, double value) {
282 if (fabs(state->step) <= tolerance) {
283 double fl, el, fr, er, unused1, unused2;
285 const double offset = tolerance - 2.0 * lambertw_ulp(value);
286 const double left = fmax(state->lo, state->w - offset);
287 const double right = fmin(state->hi, state->w + offset);
288 lambertw_residual(left, z, state->region, state->target, state->target_error,
289 &fl, &el, &unused1, &unused2);
290 lambertw_residual(right, z, state->region, state->target, state->target_error,
291 &fr, &er, &unused1, &unused2);
292 if ((state->increasing && fl + el <= 0.0 && fr - er >= 0.0) ||
293 (!state->increasing && fl - el >= 0.0 && fr + er <= 0.0)) {
294 return 1;
295 }
296 }
297 return 0;
298}
299
300static void lambertw_safeguard(
301 lmmc_lambertw_state_t* state, double f, double error, double first, double second) {
302 double candidate, denominator;
303 if (f > error) {
304 if (state->increasing) { state->hi = state->w; } else { state->lo = state->w; }
305 } else if (f < -error) {
306 if (state->increasing) { state->lo = state->w; } else { state->hi = state->w; }
307 }
308 denominator = first - 0.5 * f * (second / first);
309 candidate = state->w - f / denominator;
310 if (!isfinite(candidate) || !(candidate > state->lo && candidate < state->hi)) {
311 candidate = state->lo + (state->hi - state->lo) / 2.0;
312 }
313 state->step = candidate - state->w;
314 if (candidate == state->w && fabs(f) > error) {
315 candidate = state->lo + (state->hi - state->lo) / 2.0;
316 state->step = candidate - state->w;
317 }
318 state->w = candidate;
319}
320
321static lmmc_status_t lambertw_iterate(
322 double z, unsigned max_iterations, lmmc_lambertw_state_t* state, double* out) {
323 unsigned iteration;
324 for (iteration = 0; iteration < max_iterations; ++iteration) {
325 double f, error, first, second;
326 const double value = state->region == 1 ? state->w - 1.0 : state->w;
327 const double tolerance = 64.0 * DBL_EPSILON * fabs(value) +
328 2.0 * nextafter(0.0, 1.0);
329 lambertw_residual(state->w, z, state->region, state->target, state->target_error,
330 &f, &error, &first, &second);
331 if (!isfinite(f) || !isfinite(error)) { return LMMC_STATUS_NUMERICAL_FAILURE; }
332 if (lambertw_accept_enclosure(z, state, tolerance, value)) {
333 *out = value;
334 return LMMC_STATUS_OK;
335 }
336 lambertw_safeguard(state, f, error, first, second);
337 }
339}
340
341static lmmc_status_t lambertw_real(
342 double z, int branch, unsigned max_iterations, double* out) {
344 if (!out) { return LMMC_STATUS_INVALID_ARGUMENT; }
345 if (!isfinite(z)) { return LMMC_STATUS_NUMERICAL_FAILURE; }
346 if (z < -LMMC_INV_E || (branch == -1 && z >= 0.0)) {
348 }
349 if (z == -LMMC_INV_E) { *out = -1.0; return LMMC_STATUS_OK; }
350 if (branch == 0 && z == 0.0) { *out = z; return LMMC_STATUS_OK; }
351 if (branch == 0 && fabs(z) <= 0x1p-26) {
356 *out = fabs(z) <= DBL_EPSILON / 4.0
357 ? z : z * fma(z, fma(1.5, z, -1.0), 1.0);
358 return LMMC_STATUS_OK;
359 }
360 lambertw_initialize(z, branch, &state);
361 return lambertw_iterate(z, max_iterations, &state, out);
362}
363
365 return lambertw_real(z, 0, 100, out_res);
366}
367
369 return lambertw_real(z, -1, 100, out_res);
370}
double lmmc_real_t
Public and internal LMMC real type.
#define LMMC_PI
常量.
#define LMMC_INV_E
常量,用于 Lambert W 函数域检查.
lmmc_status_t lmmc_erfc(lmmc_real_t x, lmmc_real_t *out)
计算互补误差函数 .
lmmc_status_t lmmc_tgamma(lmmc_real_t x, lmmc_real_t *out)
计算伽马函数 ,要求 .
lmmc_status_t lmmc_beta(lmmc_real_t a, lmmc_real_t b, lmmc_real_t *out)
计算贝塔函数 ,要求 .
lmmc_status_t lmmc_lambertw_wm1(lmmc_real_t z, lmmc_real_t *out_res)
计算 Lambert W 函数 分支,要求 .
lmmc_status_t lmmc_digamma(lmmc_real_t x, lmmc_real_t *out)
计算双伽马函数 ,要求 .
lmmc_status_t lmmc_lambertw(lmmc_real_t z, lmmc_real_t *out_res)
计算 Lambert W 函数主分支 .
lmmc_status_t lmmc_erf(lmmc_real_t x, lmmc_real_t *out)
计算误差函数 .
lmmc_status_t lmmc_lgamma(lmmc_real_t x, lmmc_real_t *out)
计算对数伽马函数 ,要求 .
lmmc_status_t lmmc_log_beta(double a, double b, double *out_log_beta)
稳定计算 log B(a,b) 和 log(x^a complement_x^b / B(a,b))。 独立传入补数以保留微小上尾;负无穷表示有效下溢,失败时输出保持不变。
lmmc_status_t
LMMC 库统一返回的状态码.
@ LMMC_STATUS_CONVERGENCE_FAILED
@ LMMC_STATUS_OK
@ LMMC_STATUS_NUMERICAL_FAILURE
@ LMMC_STATUS_INVALID_ARGUMENT