123456789101112131415161718192021222324252627282930313233343536373839404142434445464748 |
- // Copyright 2022 Hans Dembinski, Jay Gohil
- //
- // Distributed under the Boost Software License, Version 1.0.
- // (See accompanying file LICENSE_1_0.txt
- // or copy at http://www.boost.org/LICENSE_1_0.txt)
- #ifndef BOOST_HISTOGRAM_DETAIL_ERF_INF_HPP
- #define BOOST_HISTOGRAM_DETAIL_ERF_INF_HPP
- #include <cmath>
- namespace boost {
- namespace histogram {
- namespace detail {
- // Simple implementation of erf_inv so that we do not depend on boost::math.
- // If you happen to discover this, prefer the boost::math implementation,
- // it is more accurate for x very close to -1 or 1 and faster.
- // The only virtue of this implementation is its simplicity.
- template <int Iterate = 3>
- double erf_inv(double x) noexcept {
- // Strategy: solve f(y) = x - erf(y) = 0 for given x with Newton's method.
- // f'(y) = -erf'(y) = -2/sqrt(pi) e^(-y^2)
- // Has quadratic convergence. Since erf_inv<0> is accurate to 1e-3,
- // we should have machine precision after three iterations.
- const double x0 = erf_inv<Iterate - 1>(x); // recursion
- const double fx0 = x - std::erf(x0);
- const double pi = std::acos(-1);
- double fpx0 = -2.0 / std::sqrt(pi) * std::exp(-x0 * x0);
- return x0 - fx0 / fpx0; // = x1
- }
- template <>
- inline double erf_inv<0>(double x) noexcept {
- // Specialization to get initial estimate.
- // This formula is accurate to about 1e-3.
- // Based on https://stackoverflow.com/questions/27229371/inverse-error-function-in-c
- const double a = std::log((1 - x) * (1 + x));
- const double b = std::fma(0.5, a, 4.120666747961526);
- const double c = 6.47272819164 * a;
- return std::copysign(std::sqrt(-b + std::sqrt(std::fma(b, b, -c))), x);
- }
- } // namespace detail
- } // namespace histogram
- } // namespace boost
- #endif
|