49 lines
1.7 KiB
C++
49 lines
1.7 KiB
C++
|
// 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
|