Chris@16: Chris@101: // Copyright Christopher Kormanyos 2002 - 2013. Chris@101: // Copyright 2011 - 2013 John Maddock. Distributed under the Boost Chris@16: // Distributed under the Boost Software License, Version 1.0. Chris@16: // (See accompanying file LICENSE_1_0.txt or copy at Chris@16: // http://www.boost.org/LICENSE_1_0.txt) Chris@16: Chris@16: // This work is based on an earlier work: Chris@16: // "Algorithm 910: A Portable C++ Multiple-Precision System for Special-Function Calculations", Chris@16: // in ACM TOMS, {VOL 37, ISSUE 4, (February 2011)} (C) ACM, 2011. http://doi.acm.org/10.1145/1916461.1916469 Chris@16: // Chris@16: // This file has no include guards or namespaces - it's expanded inline inside default_ops.hpp Chris@16: // Chris@16: Chris@16: namespace detail{ Chris@16: Chris@16: template Chris@16: inline void pow_imp(T& result, const T& t, const U& p, const mpl::false_&) Chris@16: { Chris@16: // Compute the pure power of typename T t^p. Chris@16: // Use the S-and-X binary method, as described in Chris@16: // D. E. Knuth, "The Art of Computer Programming", Vol. 2, Chris@16: // Section 4.6.3 . The resulting computational complexity Chris@16: // is order log2[abs(p)]. Chris@16: Chris@16: typedef typename boost::multiprecision::detail::canonical::type int_type; Chris@16: Chris@16: if(&result == &t) Chris@16: { Chris@16: T temp; Chris@16: pow_imp(temp, t, p, mpl::false_()); Chris@16: result = temp; Chris@16: return; Chris@16: } Chris@16: Chris@16: // This will store the result. Chris@16: if(U(p % U(2)) != U(0)) Chris@16: { Chris@16: result = t; Chris@16: } Chris@16: else Chris@16: result = int_type(1); Chris@16: Chris@16: U p2(p); Chris@16: Chris@16: // The variable x stores the binary powers of t. Chris@16: T x(t); Chris@16: Chris@16: while(U(p2 /= 2) != U(0)) Chris@16: { Chris@16: // Square x for each binary power. Chris@16: eval_multiply(x, x); Chris@16: Chris@16: const bool has_binary_power = (U(p2 % U(2)) != U(0)); Chris@16: Chris@16: if(has_binary_power) Chris@16: { Chris@16: // Multiply the result with each binary power contained in the exponent. Chris@16: eval_multiply(result, x); Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: template Chris@16: inline void pow_imp(T& result, const T& t, const U& p, const mpl::true_&) Chris@16: { Chris@16: // Signed integer power, just take care of the sign then call the unsigned version: Chris@16: typedef typename boost::multiprecision::detail::canonical::type int_type; Chris@16: typedef typename make_unsigned::type ui_type; Chris@16: Chris@16: if(p < 0) Chris@16: { Chris@16: T temp; Chris@16: temp = static_cast(1); Chris@16: T denom; Chris@16: pow_imp(denom, t, static_cast(-p), mpl::false_()); Chris@16: eval_divide(result, temp, denom); Chris@16: return; Chris@16: } Chris@16: pow_imp(result, t, static_cast(p), mpl::false_()); Chris@16: } Chris@16: Chris@16: } // namespace detail Chris@16: Chris@16: template Chris@16: inline typename enable_if >::type eval_pow(T& result, const T& t, const U& p) Chris@16: { Chris@16: detail::pow_imp(result, t, p, boost::is_signed()); Chris@16: } Chris@16: Chris@16: template Chris@16: void hyp0F0(T& H0F0, const T& x) Chris@16: { Chris@16: // Compute the series representation of Hypergeometric0F0 taken from Chris@16: // http://functions.wolfram.com/HypergeometricFunctions/Hypergeometric0F0/06/01/ Chris@16: // There are no checks on input range or parameter boundaries. Chris@16: Chris@16: typedef typename mpl::front::type ui_type; Chris@16: Chris@16: BOOST_ASSERT(&H0F0 != &x); Chris@16: long tol = boost::multiprecision::detail::digits2 >::value; Chris@16: T t; Chris@16: Chris@16: T x_pow_n_div_n_fact(x); Chris@16: Chris@16: eval_add(H0F0, x_pow_n_div_n_fact, ui_type(1)); Chris@16: Chris@16: T lim; Chris@16: eval_ldexp(lim, H0F0, 1 - tol); Chris@16: if(eval_get_sign(lim) < 0) Chris@16: lim.negate(); Chris@16: Chris@16: ui_type n; Chris@16: Chris@16: static const unsigned series_limit = Chris@16: boost::multiprecision::detail::digits2 >::value < 100 Chris@16: ? 100 : boost::multiprecision::detail::digits2 >::value; Chris@16: // Series expansion of hyperg_0f0(; ; x). Chris@16: for(n = 2; n < series_limit; ++n) Chris@16: { Chris@16: eval_multiply(x_pow_n_div_n_fact, x); Chris@16: eval_divide(x_pow_n_div_n_fact, n); Chris@16: eval_add(H0F0, x_pow_n_div_n_fact); Chris@16: bool neg = eval_get_sign(x_pow_n_div_n_fact) < 0; Chris@16: if(neg) Chris@16: x_pow_n_div_n_fact.negate(); Chris@16: if(lim.compare(x_pow_n_div_n_fact) > 0) Chris@16: break; Chris@16: if(neg) Chris@16: x_pow_n_div_n_fact.negate(); Chris@16: } Chris@16: if(n >= series_limit) Chris@16: BOOST_THROW_EXCEPTION(std::runtime_error("H0F0 failed to converge")); Chris@16: } Chris@16: Chris@16: template Chris@16: void hyp1F0(T& H1F0, const T& a, const T& x) Chris@16: { Chris@16: // Compute the series representation of Hypergeometric1F0 taken from Chris@16: // http://functions.wolfram.com/HypergeometricFunctions/Hypergeometric1F0/06/01/01/ Chris@16: // and also see the corresponding section for the power function (i.e. x^a). Chris@16: // There are no checks on input range or parameter boundaries. Chris@16: Chris@16: typedef typename boost::multiprecision::detail::canonical::type si_type; Chris@16: Chris@16: BOOST_ASSERT(&H1F0 != &x); Chris@16: BOOST_ASSERT(&H1F0 != &a); Chris@16: Chris@16: T x_pow_n_div_n_fact(x); Chris@16: T pochham_a (a); Chris@16: T ap (a); Chris@16: Chris@16: eval_multiply(H1F0, pochham_a, x_pow_n_div_n_fact); Chris@16: eval_add(H1F0, si_type(1)); Chris@16: T lim; Chris@16: eval_ldexp(lim, H1F0, 1 - boost::multiprecision::detail::digits2 >::value); Chris@16: if(eval_get_sign(lim) < 0) Chris@16: lim.negate(); Chris@16: Chris@16: si_type n; Chris@16: T term, part; Chris@16: Chris@16: static const unsigned series_limit = Chris@16: boost::multiprecision::detail::digits2 >::value < 100 Chris@16: ? 100 : boost::multiprecision::detail::digits2 >::value; Chris@16: // Series expansion of hyperg_1f0(a; ; x). Chris@16: for(n = 2; n < series_limit; n++) Chris@16: { Chris@16: eval_multiply(x_pow_n_div_n_fact, x); Chris@16: eval_divide(x_pow_n_div_n_fact, n); Chris@16: eval_increment(ap); Chris@16: eval_multiply(pochham_a, ap); Chris@16: eval_multiply(term, pochham_a, x_pow_n_div_n_fact); Chris@16: eval_add(H1F0, term); Chris@16: if(eval_get_sign(term) < 0) Chris@16: term.negate(); Chris@16: if(lim.compare(term) >= 0) Chris@16: break; Chris@16: } Chris@16: if(n >= series_limit) Chris@16: BOOST_THROW_EXCEPTION(std::runtime_error("H1F0 failed to converge")); Chris@16: } Chris@16: Chris@16: template Chris@16: void eval_exp(T& result, const T& x) Chris@16: { Chris@16: BOOST_STATIC_ASSERT_MSG(number_category::value == number_kind_floating_point, "The exp function is only valid for floating point types."); Chris@16: if(&x == &result) Chris@16: { Chris@16: T temp; Chris@16: eval_exp(temp, x); Chris@16: result = temp; Chris@16: return; Chris@16: } Chris@16: typedef typename boost::multiprecision::detail::canonical::type ui_type; Chris@16: typedef typename boost::multiprecision::detail::canonical::type si_type; Chris@16: typedef typename T::exponent_type exp_type; Chris@16: typedef typename boost::multiprecision::detail::canonical::type canonical_exp_type; Chris@16: Chris@16: // Handle special arguments. Chris@16: int type = eval_fpclassify(x); Chris@16: bool isneg = eval_get_sign(x) < 0; Chris@101: if(type == (int)FP_NAN) Chris@16: { Chris@16: result = x; Chris@16: return; Chris@16: } Chris@101: else if(type == (int)FP_INFINITE) Chris@16: { Chris@16: result = x; Chris@16: if(isneg) Chris@16: result = ui_type(0u); Chris@16: else Chris@16: result = x; Chris@16: return; Chris@16: } Chris@101: else if(type == (int)FP_ZERO) Chris@16: { Chris@16: result = ui_type(1); Chris@16: return; Chris@16: } Chris@16: Chris@16: // Get local copy of argument and force it to be positive. Chris@16: T xx = x; Chris@16: T exp_series; Chris@16: if(isneg) Chris@16: xx.negate(); Chris@16: Chris@16: // Check the range of the argument. Chris@16: if(xx.compare(si_type(1)) <= 0) Chris@16: { Chris@16: // Chris@16: // Use series for exp(x) - 1: Chris@16: // Chris@16: T lim = std::numeric_limits >::epsilon().backend(); Chris@16: unsigned k = 2; Chris@16: exp_series = xx; Chris@16: result = si_type(1); Chris@16: if(isneg) Chris@16: eval_subtract(result, exp_series); Chris@16: else Chris@16: eval_add(result, exp_series); Chris@16: eval_multiply(exp_series, xx); Chris@16: eval_divide(exp_series, ui_type(k)); Chris@16: eval_add(result, exp_series); Chris@16: while(exp_series.compare(lim) > 0) Chris@16: { Chris@16: ++k; Chris@16: eval_multiply(exp_series, xx); Chris@16: eval_divide(exp_series, ui_type(k)); Chris@16: if(isneg && (k&1)) Chris@16: eval_subtract(result, exp_series); Chris@16: else Chris@16: eval_add(result, exp_series); Chris@16: } Chris@16: return; Chris@16: } Chris@16: Chris@16: // Check for pure-integer arguments which can be either signed or unsigned. Chris@16: typename boost::multiprecision::detail::canonical::type ll; Chris@16: eval_trunc(exp_series, x); Chris@16: eval_convert_to(&ll, exp_series); Chris@16: if(x.compare(ll) == 0) Chris@16: { Chris@16: detail::pow_imp(result, get_constant_e(), ll, mpl::true_()); Chris@16: return; Chris@16: } Chris@16: Chris@16: // The algorithm for exp has been taken from MPFUN. Chris@16: // exp(t) = [ (1 + r + r^2/2! + r^3/3! + r^4/4! ...)^p2 ] * 2^n Chris@16: // where p2 is a power of 2 such as 2048, r = t_prime / p2, and Chris@16: // t_prime = t - n*ln2, with n chosen to minimize the absolute Chris@16: // value of t_prime. In the resulting Taylor series, which is Chris@16: // implemented as a hypergeometric function, |r| is bounded by Chris@16: // ln2 / p2. For small arguments, no scaling is done. Chris@16: Chris@16: // Compute the exponential series of the (possibly) scaled argument. Chris@16: Chris@16: eval_divide(result, xx, get_constant_ln2()); Chris@16: exp_type n; Chris@16: eval_convert_to(&n, result); Chris@16: Chris@16: // The scaling is 2^11 = 2048. Chris@16: static const si_type p2 = static_cast(si_type(1) << 11); Chris@16: Chris@16: eval_multiply(exp_series, get_constant_ln2(), static_cast(n)); Chris@16: eval_subtract(exp_series, xx); Chris@16: eval_divide(exp_series, p2); Chris@16: exp_series.negate(); Chris@16: hyp0F0(result, exp_series); Chris@16: Chris@16: detail::pow_imp(exp_series, result, p2, mpl::true_()); Chris@16: result = ui_type(1); Chris@16: eval_ldexp(result, result, n); Chris@16: eval_multiply(exp_series, result); Chris@16: Chris@16: if(isneg) Chris@16: eval_divide(result, ui_type(1), exp_series); Chris@16: else Chris@16: result = exp_series; Chris@16: } Chris@16: Chris@16: template Chris@16: void eval_log(T& result, const T& arg) Chris@16: { Chris@16: BOOST_STATIC_ASSERT_MSG(number_category::value == number_kind_floating_point, "The log function is only valid for floating point types."); Chris@16: // Chris@16: // We use a variation of http://dlmf.nist.gov/4.45#i Chris@16: // using frexp to reduce the argument to x * 2^n, Chris@16: // then let y = x - 1 and compute: Chris@16: // log(x) = log(2) * n + log1p(1 + y) Chris@16: // Chris@16: typedef typename boost::multiprecision::detail::canonical::type ui_type; Chris@16: typedef typename T::exponent_type exp_type; Chris@16: typedef typename boost::multiprecision::detail::canonical::type canonical_exp_type; Chris@16: typedef typename mpl::front::type fp_type; Chris@16: Chris@16: exp_type e; Chris@16: T t; Chris@16: eval_frexp(t, arg, &e); Chris@16: bool alternate = false; Chris@16: Chris@16: if(t.compare(fp_type(2) / fp_type(3)) <= 0) Chris@16: { Chris@16: alternate = true; Chris@16: eval_ldexp(t, t, 1); Chris@16: --e; Chris@16: } Chris@16: Chris@16: eval_multiply(result, get_constant_ln2(), canonical_exp_type(e)); Chris@16: INSTRUMENT_BACKEND(result); Chris@16: eval_subtract(t, ui_type(1)); /* -0.3 <= t <= 0.3 */ Chris@16: if(!alternate) Chris@16: t.negate(); /* 0 <= t <= 0.33333 */ Chris@16: T pow = t; Chris@16: T lim; Chris@16: T t2; Chris@16: Chris@16: if(alternate) Chris@16: eval_add(result, t); Chris@16: else Chris@16: eval_subtract(result, t); Chris@16: Chris@16: eval_multiply(lim, result, std::numeric_limits >::epsilon().backend()); Chris@16: if(eval_get_sign(lim) < 0) Chris@16: lim.negate(); Chris@16: INSTRUMENT_BACKEND(lim); Chris@16: Chris@16: ui_type k = 1; Chris@16: do Chris@16: { Chris@16: ++k; Chris@16: eval_multiply(pow, t); Chris@16: eval_divide(t2, pow, k); Chris@16: INSTRUMENT_BACKEND(t2); Chris@16: if(alternate && ((k & 1) != 0)) Chris@16: eval_add(result, t2); Chris@16: else Chris@16: eval_subtract(result, t2); Chris@16: INSTRUMENT_BACKEND(result); Chris@16: }while(lim.compare(t2) < 0); Chris@16: } Chris@16: Chris@16: template Chris@16: const T& get_constant_log10() Chris@16: { Chris@16: static T result; Chris@16: static bool b = false; Chris@16: if(!b) Chris@16: { Chris@16: typedef typename boost::multiprecision::detail::canonical::type ui_type; Chris@16: T ten; Chris@16: ten = ui_type(10u); Chris@16: eval_log(result, ten); Chris@16: } Chris@16: Chris@16: constant_initializer >::do_nothing(); Chris@16: Chris@16: return result; Chris@16: } Chris@16: Chris@16: template Chris@16: void eval_log10(T& result, const T& arg) Chris@16: { Chris@16: BOOST_STATIC_ASSERT_MSG(number_category::value == number_kind_floating_point, "The log10 function is only valid for floating point types."); Chris@16: eval_log(result, arg); Chris@16: eval_divide(result, get_constant_log10()); Chris@16: } Chris@16: Chris@16: template Chris@16: inline void eval_pow(T& result, const T& x, const T& a) Chris@16: { Chris@16: BOOST_STATIC_ASSERT_MSG(number_category::value == number_kind_floating_point, "The pow function is only valid for floating point types."); Chris@16: typedef typename boost::multiprecision::detail::canonical::type si_type; Chris@16: typedef typename mpl::front::type fp_type; Chris@16: Chris@16: if((&result == &x) || (&result == &a)) Chris@16: { Chris@16: T t; Chris@16: eval_pow(t, x, a); Chris@16: result = t; Chris@16: return; Chris@16: } Chris@16: Chris@16: if(a.compare(si_type(1)) == 0) Chris@16: { Chris@16: result = x; Chris@16: return; Chris@16: } Chris@16: Chris@16: int type = eval_fpclassify(x); Chris@16: Chris@16: switch(type) Chris@16: { Chris@16: case FP_INFINITE: Chris@16: result = x; Chris@16: return; Chris@16: case FP_ZERO: Chris@16: switch(eval_fpclassify(a)) Chris@16: { Chris@16: case FP_ZERO: Chris@16: result = si_type(1); Chris@16: break; Chris@16: case FP_NAN: Chris@16: result = a; Chris@16: break; Chris@16: default: Chris@16: result = x; Chris@16: break; Chris@16: } Chris@16: return; Chris@16: case FP_NAN: Chris@16: result = x; Chris@16: return; Chris@16: default: ; Chris@16: } Chris@16: Chris@16: int s = eval_get_sign(a); Chris@16: if(s == 0) Chris@16: { Chris@16: result = si_type(1); Chris@16: return; Chris@16: } Chris@16: Chris@16: if(s < 0) Chris@16: { Chris@16: T t, da; Chris@16: t = a; Chris@16: t.negate(); Chris@16: eval_pow(da, x, t); Chris@16: eval_divide(result, si_type(1), da); Chris@16: return; Chris@16: } Chris@16: Chris@16: typename boost::multiprecision::detail::canonical::type an; Chris@16: T fa; Chris@16: try Chris@16: { Chris@16: eval_convert_to(&an, a); Chris@16: if(a.compare(an) == 0) Chris@16: { Chris@16: detail::pow_imp(result, x, an, mpl::true_()); Chris@16: return; Chris@16: } Chris@16: } Chris@16: catch(const std::exception&) Chris@16: { Chris@16: // conversion failed, just fall through, value is not an integer. Chris@16: an = (std::numeric_limits::max)(); Chris@16: } Chris@16: Chris@16: if((eval_get_sign(x) < 0)) Chris@16: { Chris@16: typename boost::multiprecision::detail::canonical::type aun; Chris@16: try Chris@16: { Chris@16: eval_convert_to(&aun, a); Chris@16: if(a.compare(aun) == 0) Chris@16: { Chris@16: fa = x; Chris@16: fa.negate(); Chris@16: eval_pow(result, fa, a); Chris@16: if(aun & 1u) Chris@16: result.negate(); Chris@16: return; Chris@16: } Chris@16: } Chris@16: catch(const std::exception&) Chris@16: { Chris@16: // conversion failed, just fall through, value is not an integer. Chris@16: } Chris@16: if(std::numeric_limits >::has_quiet_NaN) Chris@16: result = std::numeric_limits >::quiet_NaN().backend(); Chris@16: else Chris@16: { Chris@16: BOOST_THROW_EXCEPTION(std::domain_error("Result of pow is undefined or non-real and there is no NaN for this number type.")); Chris@16: } Chris@16: return; Chris@16: } Chris@16: Chris@16: T t, da; Chris@16: Chris@16: eval_subtract(da, a, an); Chris@16: Chris@16: if((x.compare(fp_type(0.5)) >= 0) && (x.compare(fp_type(0.9)) < 0)) Chris@16: { Chris@16: if(a.compare(fp_type(1e-5f)) <= 0) Chris@16: { Chris@16: // Series expansion for small a. Chris@16: eval_log(t, x); Chris@16: eval_multiply(t, a); Chris@16: hyp0F0(result, t); Chris@16: return; Chris@16: } Chris@16: else Chris@16: { Chris@16: // Series expansion for moderately sized x. Note that for large power of a, Chris@16: // the power of the integer part of a is calculated using the pown function. Chris@16: if(an) Chris@16: { Chris@16: da.negate(); Chris@16: t = si_type(1); Chris@16: eval_subtract(t, x); Chris@16: hyp1F0(result, da, t); Chris@16: detail::pow_imp(t, x, an, mpl::true_()); Chris@16: eval_multiply(result, t); Chris@16: } Chris@16: else Chris@16: { Chris@16: da = a; Chris@16: da.negate(); Chris@16: t = si_type(1); Chris@16: eval_subtract(t, x); Chris@16: hyp1F0(result, da, t); Chris@16: } Chris@16: } Chris@16: } Chris@16: else Chris@16: { Chris@16: // Series expansion for pow(x, a). Note that for large power of a, the power Chris@16: // of the integer part of a is calculated using the pown function. Chris@16: if(an) Chris@16: { Chris@16: eval_log(t, x); Chris@16: eval_multiply(t, da); Chris@16: eval_exp(result, t); Chris@16: detail::pow_imp(t, x, an, mpl::true_()); Chris@16: eval_multiply(result, t); Chris@16: } Chris@16: else Chris@16: { Chris@16: eval_log(t, x); Chris@16: eval_multiply(t, a); Chris@16: eval_exp(result, t); Chris@16: } Chris@16: } Chris@16: } Chris@16: Chris@16: template Chris@16: inline typename enable_if, void>::type eval_pow(T& result, const T& x, const A& a) Chris@16: { Chris@16: // Note this one is restricted to float arguments since pow.hpp already has a version for Chris@16: // integer powers.... Chris@16: typedef typename boost::multiprecision::detail::canonical::type canonical_type; Chris@16: typedef typename mpl::if_, T, canonical_type>::type cast_type; Chris@16: cast_type c; Chris@16: c = a; Chris@16: eval_pow(result, x, c); Chris@16: } Chris@16: Chris@16: template Chris@16: inline typename enable_if, void>::type eval_pow(T& result, const A& x, const T& a) Chris@16: { Chris@16: typedef typename boost::multiprecision::detail::canonical::type canonical_type; Chris@16: typedef typename mpl::if_, T, canonical_type>::type cast_type; Chris@16: cast_type c; Chris@16: c = x; Chris@16: eval_pow(result, c, a); Chris@16: } Chris@16: Chris@16: namespace detail{ Chris@16: Chris@16: template Chris@16: void small_sinh_series(T x, T& result) Chris@16: { Chris@16: typedef typename boost::multiprecision::detail::canonical::type ui_type; Chris@16: bool neg = eval_get_sign(x) < 0; Chris@16: if(neg) Chris@16: x.negate(); Chris@16: T p(x); Chris@16: T mult(x); Chris@16: eval_multiply(mult, x); Chris@16: result = x; Chris@16: ui_type k = 1; Chris@16: Chris@16: T lim(x); Chris@16: eval_ldexp(lim, lim, 1 - boost::multiprecision::detail::digits2 >::value); Chris@16: Chris@16: do Chris@16: { Chris@16: eval_multiply(p, mult); Chris@16: eval_divide(p, ++k); Chris@16: eval_divide(p, ++k); Chris@16: eval_add(result, p); Chris@16: }while(p.compare(lim) >= 0); Chris@16: if(neg) Chris@16: result.negate(); Chris@16: } Chris@16: Chris@16: template Chris@16: void sinhcosh(const T& x, T* p_sinh, T* p_cosh) Chris@16: { Chris@16: typedef typename boost::multiprecision::detail::canonical::type ui_type; Chris@16: typedef typename mpl::front::type fp_type; Chris@16: Chris@16: switch(eval_fpclassify(x)) Chris@16: { Chris@16: case FP_NAN: Chris@16: case FP_INFINITE: Chris@16: if(p_sinh) Chris@16: *p_sinh = x; Chris@16: if(p_cosh) Chris@16: { Chris@16: *p_cosh = x; Chris@16: if(eval_get_sign(x) < 0) Chris@16: p_cosh->negate(); Chris@16: } Chris@16: return; Chris@16: case FP_ZERO: Chris@16: if(p_sinh) Chris@16: *p_sinh = x; Chris@16: if(p_cosh) Chris@16: *p_cosh = ui_type(1); Chris@16: return; Chris@16: default: ; Chris@16: } Chris@16: Chris@16: bool small_sinh = eval_get_sign(x) < 0 ? x.compare(fp_type(-0.5)) > 0 : x.compare(fp_type(0.5)) < 0; Chris@16: Chris@16: if(p_cosh || !small_sinh) Chris@16: { Chris@16: T e_px, e_mx; Chris@16: eval_exp(e_px, x); Chris@16: eval_divide(e_mx, ui_type(1), e_px); Chris@16: Chris@16: if(p_sinh) Chris@16: { Chris@16: if(small_sinh) Chris@16: { Chris@16: small_sinh_series(x, *p_sinh); Chris@16: } Chris@16: else Chris@16: { Chris@16: eval_subtract(*p_sinh, e_px, e_mx); Chris@16: eval_ldexp(*p_sinh, *p_sinh, -1); Chris@16: } Chris@16: } Chris@16: if(p_cosh) Chris@16: { Chris@16: eval_add(*p_cosh, e_px, e_mx); Chris@16: eval_ldexp(*p_cosh, *p_cosh, -1); Chris@16: } Chris@16: } Chris@16: else Chris@16: { Chris@16: small_sinh_series(x, *p_sinh); Chris@16: } Chris@16: } Chris@16: Chris@16: } // namespace detail Chris@16: Chris@16: template Chris@16: inline void eval_sinh(T& result, const T& x) Chris@16: { Chris@16: BOOST_STATIC_ASSERT_MSG(number_category::value == number_kind_floating_point, "The sinh function is only valid for floating point types."); Chris@16: detail::sinhcosh(x, &result, static_cast(0)); Chris@16: } Chris@16: Chris@16: template Chris@16: inline void eval_cosh(T& result, const T& x) Chris@16: { Chris@16: BOOST_STATIC_ASSERT_MSG(number_category::value == number_kind_floating_point, "The cosh function is only valid for floating point types."); Chris@16: detail::sinhcosh(x, static_cast(0), &result); Chris@16: } Chris@16: Chris@16: template Chris@16: inline void eval_tanh(T& result, const T& x) Chris@16: { Chris@16: BOOST_STATIC_ASSERT_MSG(number_category::value == number_kind_floating_point, "The tanh function is only valid for floating point types."); Chris@16: T c; Chris@16: detail::sinhcosh(x, &result, &c); Chris@16: eval_divide(result, c); Chris@16: } Chris@16: