35 static const double coeff[9] = {
43 9.9843695780195716e-6,
54 double sinpx = sin(
LMMC_PI * x);
59 *out = log(
LMMC_PI) - log(fabs(sinpx)) - lg1mx;
66 for (i = 1; i < 9; i++) {
67 ser += coeff[i] / (x + (double)i);
70 *out = 0.5 * log(2.0 *
LMMC_PI) + (x + 0.5) * log(tmp) - tmp + log(ser);
93 double logarithm, value;
99 value = exp(logarithm);
127 double ix2 = ix * ix;
130 static const double bernoulli_coeff[] = {
143 for (k = 0; k < 7; k++) {
144 sum -= bernoulli_coeff[k] * ix2k;
148 result += log(x) - 0.5 * ix + sum;
159static double lambertw_ulp(
double value) {
160 const double magnitude = fabs(value);
161 return nextafter(magnitude, INFINITY) - magnitude;
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;
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
190 double p, absolute, square, value, exponential;
194 for (k = 29; k >= 0; --k) {
195 p = fma(p, w, coefficient[k]);
196 absolute = fma(absolute, fabs(w), coefficient[k]);
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));
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;
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);
238 state->
step = INFINITY;
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;
245 state->
target = lmmc_two_sum(high, low, &remainder);
247 DBL_EPSILON * (fabs(high) + fabs(low)) + 2.0e-32 * fabs(z);
253 (2.0 / 3.0) * state->
target;
254 }
else if (branch == -1 || z > 1.0) {
257 state->
target = log(fabs(z));
266 state->
w = z <= 2.718281828459045 ? 1.0 : state->
target - log(state->
target);
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);
275 if (!(state->
w > state->
lo && state->
w < state->
hi)) {
276 state->
w = state->
lo + (state->
hi - state->
lo) / 2.0;
280static int lambertw_accept_enclosure(
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);
289 &fl, &el, &unused1, &unused2);
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)) {
300static void lambertw_safeguard(
302 double candidate, denominator;
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; }
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;
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;
318 state->
w = candidate;
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);
330 &f, &error, &first, &second);
332 if (lambertw_accept_enclosure(z, state, tolerance, value)) {
336 lambertw_safeguard(state, f, error, first, second);
342 double z,
int branch,
unsigned max_iterations,
double* out) {
346 if (z < -
LMMC_INV_E || (branch == -1 && z >= 0.0)) {
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);
360 lambertw_initialize(z, branch, &state);
361 return lambertw_iterate(z, max_iterations, &state, out);
365 return lambertw_real(z, 0, 100, out_res);
369 return lambertw_real(z, -1, 100, out_res);
double lmmc_real_t
Public and internal LMMC real type.
#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_NUMERICAL_FAILURE
@ LMMC_STATUS_INVALID_ARGUMENT