summaryrefslogtreecommitdiff
path: root/boost/math/special_functions/detail/bessel_ik.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'boost/math/special_functions/detail/bessel_ik.hpp')
-rw-r--r--boost/math/special_functions/detail/bessel_ik.hpp23
1 files changed, 22 insertions, 1 deletions
diff --git a/boost/math/special_functions/detail/bessel_ik.hpp b/boost/math/special_functions/detail/bessel_ik.hpp
index a589673ffb..10118d9715 100644
--- a/boost/math/special_functions/detail/bessel_ik.hpp
+++ b/boost/math/special_functions/detail/bessel_ik.hpp
@@ -234,6 +234,7 @@ int CF2_ik(T v, T x, T* Kv, T* Kv1, const Policy& pol)
BOOST_MATH_INSTRUMENT_VARIABLE(b);
BOOST_MATH_INSTRUMENT_VARIABLE(D);
BOOST_MATH_INSTRUMENT_VARIABLE(f);
+
for (k = 2; k < policies::get_max_series_iterations<Policy>(); k++) // starting from 2
{
// continued fraction f = z1 / z0
@@ -250,10 +251,27 @@ int CF2_ik(T v, T x, T* Kv, T* Kv1, const Policy& pol)
C *= -a / k;
Q += C * q;
S += Q * delta;
+ //
+ // Under some circumstances q can grow very small and C very
+ // large, leading to under/overflow. This is particularly an
+ // issue for types which have many digits precision but a narrow
+ // exponent range. A typical example being a "double double" type.
+ // To avoid this situation we can normalise q (and related prev/current)
+ // and C. All other variables remain unchanged in value. A typical
+ // test case occurs when x is close to 2, for example cyl_bessel_k(9.125, 2.125).
+ //
+ if(q < tools::epsilon<T>())
+ {
+ C *= q;
+ prev /= q;
+ current /= q;
+ q = 1;
+ }
// S converges slower than f
BOOST_MATH_INSTRUMENT_VARIABLE(Q * delta);
BOOST_MATH_INSTRUMENT_VARIABLE(abs(S) * tolerance);
+ BOOST_MATH_INSTRUMENT_VARIABLE(S);
if (abs(Q * delta) < abs(S) * tolerance)
{
break;
@@ -261,7 +279,10 @@ int CF2_ik(T v, T x, T* Kv, T* Kv1, const Policy& pol)
}
policies::check_series_iterations<T>("boost::math::bessel_ik<%1%>(%1%,%1%) in CF2_ik", k, pol);
- *Kv = sqrt(pi<T>() / (2 * x)) * exp(-x) / S;
+ if(x >= tools::log_max_value<T>())
+ *Kv = exp(0.5f * log(pi<T>() / (2 * x)) - x - log(S));
+ else
+ *Kv = sqrt(pi<T>() / (2 * x)) * exp(-x) / S;
*Kv1 = *Kv * (0.5f + v + x + (v * v - 0.25f) * f) / x;
BOOST_MATH_INSTRUMENT_VARIABLE(*Kv);
BOOST_MATH_INSTRUMENT_VARIABLE(*Kv1);