| @@ -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(); | 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) | template<typename T> real_t<T> erf(real_t<T> const &x) | ||||
| { | { | ||||
| /* Strategy for erf(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)) | if (x < real_t<T>(7)) | ||||
| return real_t<T>::R_1() - erf(x); | 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) | 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 sum = real_t<T>::R_0(); | ||||
| auto x2 = x * x; | auto x2 = x * x; | ||||
| if (x < real_t<T>(7)) | if (x < real_t<T>(7)) | ||||
| return exp(x2) * (real_t<T>::R_1() - erf(x)); | 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) | 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 | // 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 | // 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; | return (expm1(x) - expm1(-x)) / 2; | ||||
| } | } | ||||