|
|
|
|
|
#pragma once |
|
|
|
#include "config.h" |
|
#include "tools.h" |
|
#include "error.h" |
|
#include "cephes/dd_real.h" |
|
|
|
namespace xsf { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <class T> |
|
struct IvRatioCFTailGenerator { |
|
|
|
XSF_HOST_DEVICE IvRatioCFTailGenerator(T vc, T xc, T c) noexcept { |
|
a0_ = -(2*vc-c)*xc; |
|
as_ = -2*c*xc; |
|
b0_ = 2*(vc+xc); |
|
bs_ = c; |
|
k_ = 0; |
|
} |
|
|
|
XSF_HOST_DEVICE std::pair<T, T> operator()() noexcept { |
|
using std::fma; |
|
++k_; |
|
return {fma(static_cast<T>(k_), as_, a0_), |
|
fma(static_cast<T>(k_), bs_, b0_)}; |
|
} |
|
|
|
private: |
|
T a0_, as_; |
|
T b0_, bs_; |
|
std::uint64_t k_; |
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
template <class T> |
|
XSF_HOST_DEVICE inline std::pair<double, std::uint64_t> |
|
_iv_ratio_cf(double v, double x, bool complement) { |
|
|
|
int e; |
|
std::frexp(std::fmax(v, x), &e); |
|
T c = T(std::ldexp(1, 2-e)); |
|
T vc = v * c; |
|
T xc = x * c; |
|
|
|
IvRatioCFTailGenerator<T> cf(vc, xc, c); |
|
auto [fc, terms] = detail::series_eval_kahan( |
|
detail::continued_fraction_series(cf), |
|
T(std::numeric_limits<double>::epsilon()), |
|
1000, |
|
2*vc); |
|
|
|
T ret = (complement ? fc : xc) / (xc + fc); |
|
return {static_cast<double>(ret), terms}; |
|
} |
|
|
|
XSF_HOST_DEVICE inline double iv_ratio(double v, double x) { |
|
|
|
if (std::isnan(v) || std::isnan(x)) { |
|
return std::numeric_limits<double>::quiet_NaN(); |
|
} |
|
if (v < 0.5 || x < 0) { |
|
set_error("iv_ratio", SF_ERROR_DOMAIN, NULL); |
|
return std::numeric_limits<double>::quiet_NaN(); |
|
} |
|
if (std::isinf(v) && std::isinf(x)) { |
|
|
|
set_error("iv_ratio", SF_ERROR_DOMAIN, NULL); |
|
return std::numeric_limits<double>::quiet_NaN(); |
|
} |
|
if (x == 0.0) { |
|
return x; |
|
} |
|
if (std::isinf(v)) { |
|
return 0.0; |
|
} |
|
if (std::isinf(x)) { |
|
return 1.0; |
|
} |
|
|
|
auto [ret, terms] = _iv_ratio_cf<double>(v, x, false); |
|
if (terms == 0) { |
|
set_error("iv_ratio", SF_ERROR_NO_RESULT, NULL); |
|
return std::numeric_limits<double>::quiet_NaN(); |
|
} |
|
return ret; |
|
} |
|
|
|
XSF_HOST_DEVICE inline float iv_ratio(float v, float x) { |
|
return iv_ratio(static_cast<double>(v), static_cast<double>(x)); |
|
} |
|
|
|
XSF_HOST_DEVICE inline double iv_ratio_c(double v, double x) { |
|
|
|
if (std::isnan(v) || std::isnan(x)) { |
|
return std::numeric_limits<double>::quiet_NaN(); |
|
} |
|
if (v < 0.5 || x < 0) { |
|
set_error("iv_ratio_c", SF_ERROR_DOMAIN, NULL); |
|
return std::numeric_limits<double>::quiet_NaN(); |
|
} |
|
if (std::isinf(v) && std::isinf(x)) { |
|
|
|
set_error("iv_ratio_c", SF_ERROR_DOMAIN, NULL); |
|
return std::numeric_limits<double>::quiet_NaN(); |
|
} |
|
if (x == 0.0) { |
|
return 1.0; |
|
} |
|
if (std::isinf(v)) { |
|
return 1.0; |
|
} |
|
if (std::isinf(x)) { |
|
return 0.0; |
|
} |
|
|
|
if (v >= 1) { |
|
|
|
|
|
auto [ret, terms] = _iv_ratio_cf<double>(v, x, true); |
|
if (terms == 0) { |
|
set_error("iv_ratio_c", SF_ERROR_NO_RESULT, NULL); |
|
return std::numeric_limits<double>::quiet_NaN(); |
|
} |
|
return ret; |
|
} else if (v > 0.5) { |
|
|
|
|
|
using cephes::detail::double_double; |
|
auto [ret, terms] = _iv_ratio_cf<double_double>(v, x, true); |
|
if (terms == 0) { |
|
set_error("iv_ratio_c", SF_ERROR_NO_RESULT, NULL); |
|
return std::numeric_limits<double>::quiet_NaN(); |
|
} |
|
return ret; |
|
} else { |
|
|
|
|
|
double t = std::exp(-2*x); |
|
return (2 * t) / (1 + t); |
|
} |
|
} |
|
|
|
XSF_HOST_DEVICE inline float iv_ratio_c(float v, float x) { |
|
return iv_ratio_c(static_cast<double>(v), static_cast<double>(x)); |
|
} |
|
|
|
} |
|
|