|
|
@@ -1161,6 +1161,28 @@ template<typename T> real_t<T> expm1(real_t<T> const &x) |
|
|
|
return near_zero ? fast_exp_sub(x, real_t<T>::R_1()) : exp(x) - real_t<T>::R_1(); |
|
|
|
} |
|
|
|
|
|
|
|
template<typename T> real_t<T> fast_erfcx(real_t<T> const &x, real_t<T> const &x2) |
|
|
|
{ |
|
|
|
auto sum = real_t<T>::R_0(); |
|
|
|
|
|
|
|
// XXX: The series does not converge, there is an optimal value around |
|
|
|
// ⌊x²+0.5⌋. If maximum accuracy cannot be reached, we must stop summing |
|
|
|
// when divergence is detected. |
|
|
|
auto prev = x2; |
|
|
|
auto xn = real_t<T>::R_1(), xmul = inverse(x2 + x2); |
|
|
|
for (int n = 0;; ++n, xn *= xmul) |
|
|
|
{ |
|
|
|
auto tmp = xn * fast_fact<T>(n * 2 - 1, 2); |
|
|
|
auto newsum = (n & 1) ? sum - tmp : sum + tmp; |
|
|
|
if (newsum == sum || tmp > prev) |
|
|
|
break; |
|
|
|
sum = newsum; |
|
|
|
prev = tmp; |
|
|
|
} |
|
|
|
|
|
|
|
return sum / (x * sqrt(real_t<T>::R_PI())); |
|
|
|
} |
|
|
|
|
|
|
|
template<typename T> real_t<T> erf(real_t<T> const &x) |
|
|
|
{ |
|
|
|
/* Strategy for erf(x): |
|
|
@@ -1206,43 +1228,29 @@ template<typename T> real_t<T> erfc(real_t<T> const &x) |
|
|
|
if (x < real_t<T>(7)) |
|
|
|
return real_t<T>::R_1() - erf(x); |
|
|
|
|
|
|
|
return exp(-x * x) * erfcx(x); |
|
|
|
auto x2 = x * x; |
|
|
|
return exp(-x2) * fast_erfcx(x, x2); |
|
|
|
} |
|
|
|
|
|
|
|
template<typename T> real_t<T> erfcx(real_t<T> const &x) |
|
|
|
{ |
|
|
|
// Strategy for erfc(x): |
|
|
|
// - if x<7, erfc(x) = exp(x²)·(1-erf(x)) |
|
|
|
// - if x≥7, erfc(x) = sum((-1)^n·(2n-1)!!/(2x²)^n) / (x·sqrt(π)) |
|
|
|
// Strategy for erfcx(x): |
|
|
|
// - if x<7, erfcx(x) = exp(x²)·(1-erf(x)) |
|
|
|
// - if x≥7, erfcx(x) = sum((-1)^n·(2n-1)!!/(2x²)^n) / (x·sqrt(π)) |
|
|
|
auto sum = real_t<T>::R_0(); |
|
|
|
auto x2 = x * x; |
|
|
|
|
|
|
|
if (x < real_t<T>(7)) |
|
|
|
return exp(x2) * (real_t<T>::R_1() - erf(x)); |
|
|
|
|
|
|
|
// XXX: The series does not converge, there is an optimal value around |
|
|
|
// ⌊x²+0.5⌋. If maximum accuracy cannot be reached, we must stop summing |
|
|
|
// when divergence is detected. |
|
|
|
auto prev = x2; |
|
|
|
auto xn = real_t<T>::R_1(), xmul = inverse(x2 + x2); |
|
|
|
for (int n = 0;; ++n, xn *= xmul) |
|
|
|
{ |
|
|
|
auto tmp = xn * fast_fact<T>(n * 2 - 1, 2); |
|
|
|
auto newsum = (n & 1) ? sum - tmp : sum + tmp; |
|
|
|
if (newsum == sum || tmp > prev) |
|
|
|
break; |
|
|
|
sum = newsum; |
|
|
|
prev = tmp; |
|
|
|
} |
|
|
|
|
|
|
|
return sum / (x * sqrt(real_t<T>::R_PI())); |
|
|
|
return fast_erfcx(x, x2); |
|
|
|
} |
|
|
|
|
|
|
|
template<typename T> real_t<T> sinh(real_t<T> const &x) |
|
|
|
{ |
|
|
|
// Use expm1() to ensure high accuracy around 0. No need to worry about |
|
|
|
// accuracy in other ranges because either exp(x) or exp(-x) will be |
|
|
|
// very large and cancel the other term. |
|
|
|
// large enough to cancel the other term. |
|
|
|
return (expm1(x) - expm1(-x)) / 2; |
|
|
|
} |
|
|
|
|
|
|
|