Chris@16: Chris@16: // Copyright Christopher Kormanyos 2002 - 2011. Chris@16: // Copyright 2011 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: template Chris@16: void hyp0F1(T& result, const T& b, const T& x) Chris@16: { Chris@16: typedef typename boost::multiprecision::detail::canonical::type si_type; Chris@16: typedef typename boost::multiprecision::detail::canonical::type ui_type; Chris@16: Chris@16: // Compute the series representation of Hypergeometric0F1 taken from Chris@16: // http://functions.wolfram.com/HypergeometricFunctions/Hypergeometric0F1/06/01/01/ Chris@16: // There are no checks on input range or parameter boundaries. Chris@16: Chris@16: T x_pow_n_div_n_fact(x); Chris@16: T pochham_b (b); Chris@16: T bp (b); Chris@16: Chris@16: eval_divide(result, x_pow_n_div_n_fact, pochham_b); Chris@16: eval_add(result, ui_type(1)); Chris@16: Chris@16: si_type n; Chris@16: Chris@16: T tol; Chris@16: tol = ui_type(1); Chris@16: eval_ldexp(tol, tol, 1 - boost::multiprecision::detail::digits2 >::value); Chris@16: eval_multiply(tol, result); Chris@16: if(eval_get_sign(tol) < 0) Chris@16: tol.negate(); Chris@16: T term; Chris@16: Chris@101: static const int series_limit = Chris@16: boost::multiprecision::detail::digits2 >::value < 100 Chris@16: ? 100 : boost::multiprecision::detail::digits2 >::value; Chris@16: // Series expansion of hyperg_0f1(; b; 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(bp); Chris@16: eval_multiply(pochham_b, bp); Chris@16: Chris@16: eval_divide(term, x_pow_n_div_n_fact, pochham_b); Chris@16: eval_add(result, term); Chris@16: Chris@16: bool neg_term = eval_get_sign(term) < 0; Chris@16: if(neg_term) Chris@16: term.negate(); Chris@16: if(term.compare(tol) <= 0) Chris@16: break; Chris@16: } Chris@16: Chris@16: if(n >= series_limit) Chris@16: BOOST_THROW_EXCEPTION(std::runtime_error("H0F1 Failed to Converge")); Chris@16: } Chris@16: Chris@16: Chris@16: template Chris@16: void eval_sin(T& result, const T& x) Chris@16: { Chris@16: BOOST_STATIC_ASSERT_MSG(number_category::value == number_kind_floating_point, "The sin function is only valid for floating point types."); Chris@16: if(&result == &x) Chris@16: { Chris@16: T temp; Chris@16: eval_sin(temp, x); Chris@16: result = temp; Chris@16: return; Chris@16: } Chris@16: Chris@16: typedef typename boost::multiprecision::detail::canonical::type si_type; 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_INFINITE: Chris@16: case FP_NAN: Chris@16: if(std::numeric_limits >::has_quiet_NaN) Chris@16: result = std::numeric_limits >::quiet_NaN().backend(); Chris@16: else Chris@16: BOOST_THROW_EXCEPTION(std::domain_error("Result is undefined or complex and there is no NaN for this number type.")); Chris@16: return; Chris@16: case FP_ZERO: Chris@16: result = ui_type(0); Chris@16: return; Chris@16: default: ; Chris@16: } Chris@16: Chris@16: // Local copy of the argument Chris@16: T xx = x; Chris@16: Chris@16: // Analyze and prepare the phase of the argument. Chris@16: // Make a local, positive copy of the argument, xx. Chris@16: // The argument xx will be reduced to 0 <= xx <= pi/2. Chris@16: bool b_negate_sin = false; Chris@16: Chris@16: if(eval_get_sign(x) < 0) Chris@16: { Chris@16: xx.negate(); Chris@16: b_negate_sin = !b_negate_sin; Chris@16: } Chris@16: Chris@16: T n_pi, t; Chris@16: // Remove even multiples of pi. Chris@16: if(xx.compare(get_constant_pi()) > 0) Chris@16: { Chris@16: eval_divide(n_pi, xx, get_constant_pi()); Chris@16: eval_trunc(n_pi, n_pi); Chris@16: t = ui_type(2); Chris@16: eval_fmod(t, n_pi, t); Chris@16: const bool b_n_pi_is_even = eval_get_sign(t) == 0; Chris@16: eval_multiply(n_pi, get_constant_pi()); Chris@16: eval_subtract(xx, n_pi); Chris@16: Chris@16: BOOST_MATH_INSTRUMENT_CODE(xx.str(0, std::ios_base::scientific)); Chris@16: BOOST_MATH_INSTRUMENT_CODE(n_pi.str(0, std::ios_base::scientific)); Chris@16: Chris@16: // Adjust signs if the multiple of pi is not even. Chris@16: if(!b_n_pi_is_even) Chris@16: { Chris@16: b_negate_sin = !b_negate_sin; Chris@16: } Chris@16: } Chris@16: Chris@16: // Reduce the argument to 0 <= xx <= pi/2. Chris@16: eval_ldexp(t, get_constant_pi(), -1); Chris@16: if(xx.compare(t) > 0) Chris@16: { Chris@16: eval_subtract(xx, get_constant_pi(), xx); Chris@16: BOOST_MATH_INSTRUMENT_CODE(xx.str(0, std::ios_base::scientific)); Chris@16: } Chris@16: Chris@16: eval_subtract(t, xx); Chris@16: const bool b_zero = eval_get_sign(xx) == 0; Chris@16: const bool b_pi_half = eval_get_sign(t) == 0; Chris@16: Chris@16: // Check if the reduced argument is very close to 0 or pi/2. Chris@16: const bool b_near_zero = xx.compare(fp_type(1e-1)) < 0; Chris@16: const bool b_near_pi_half = t.compare(fp_type(1e-1)) < 0;; Chris@16: Chris@16: if(b_zero) Chris@16: { Chris@16: result = ui_type(0); Chris@16: } Chris@16: else if(b_pi_half) Chris@16: { Chris@16: result = ui_type(1); Chris@16: } Chris@16: else if(b_near_zero) Chris@16: { Chris@16: eval_multiply(t, xx, xx); Chris@16: eval_divide(t, si_type(-4)); Chris@16: T t2; Chris@16: t2 = fp_type(1.5); Chris@16: hyp0F1(result, t2, t); Chris@16: BOOST_MATH_INSTRUMENT_CODE(result.str(0, std::ios_base::scientific)); Chris@16: eval_multiply(result, xx); Chris@16: } Chris@16: else if(b_near_pi_half) Chris@16: { Chris@16: eval_multiply(t, t); Chris@16: eval_divide(t, si_type(-4)); Chris@16: T t2; Chris@16: t2 = fp_type(0.5); Chris@16: hyp0F1(result, t2, t); Chris@16: BOOST_MATH_INSTRUMENT_CODE(result.str(0, std::ios_base::scientific)); Chris@16: } Chris@16: else Chris@16: { Chris@16: // Scale to a small argument for an efficient Taylor series, Chris@16: // implemented as a hypergeometric function. Use a standard Chris@16: // divide by three identity a certain number of times. Chris@16: // Here we use division by 3^9 --> (19683 = 3^9). Chris@16: Chris@16: static const si_type n_scale = 9; Chris@16: static const si_type n_three_pow_scale = static_cast(19683L); Chris@16: Chris@16: eval_divide(xx, n_three_pow_scale); Chris@16: Chris@16: // Now with small arguments, we are ready for a series expansion. Chris@16: eval_multiply(t, xx, xx); Chris@16: eval_divide(t, si_type(-4)); Chris@16: T t2; Chris@16: t2 = fp_type(1.5); Chris@16: hyp0F1(result, t2, t); Chris@16: BOOST_MATH_INSTRUMENT_CODE(result.str(0, std::ios_base::scientific)); Chris@16: eval_multiply(result, xx); Chris@16: Chris@16: // Convert back using multiple angle identity. Chris@16: for(boost::int32_t k = static_cast(0); k < n_scale; k++) Chris@16: { Chris@16: // Rescale the cosine value using the multiple angle identity. Chris@16: eval_multiply(t2, result, ui_type(3)); Chris@16: eval_multiply(t, result, result); Chris@16: eval_multiply(t, result); Chris@16: eval_multiply(t, ui_type(4)); Chris@16: eval_subtract(result, t2, t); Chris@16: } Chris@16: } Chris@16: Chris@16: if(b_negate_sin) Chris@16: result.negate(); Chris@16: } Chris@16: Chris@16: template Chris@16: void eval_cos(T& result, const T& x) Chris@16: { Chris@16: BOOST_STATIC_ASSERT_MSG(number_category::value == number_kind_floating_point, "The cos function is only valid for floating point types."); Chris@16: if(&result == &x) Chris@16: { Chris@16: T temp; Chris@16: eval_cos(temp, x); Chris@16: result = temp; Chris@16: return; Chris@16: } Chris@16: Chris@16: typedef typename boost::multiprecision::detail::canonical::type si_type; 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_INFINITE: Chris@16: case FP_NAN: Chris@16: if(std::numeric_limits >::has_quiet_NaN) Chris@16: result = std::numeric_limits >::quiet_NaN().backend(); Chris@16: else Chris@16: BOOST_THROW_EXCEPTION(std::domain_error("Result is undefined or complex and there is no NaN for this number type.")); Chris@16: return; Chris@16: case FP_ZERO: Chris@16: result = ui_type(1); Chris@16: return; Chris@16: default: ; Chris@16: } Chris@16: Chris@16: // Local copy of the argument Chris@16: T xx = x; Chris@16: Chris@16: // Analyze and prepare the phase of the argument. Chris@16: // Make a local, positive copy of the argument, xx. Chris@16: // The argument xx will be reduced to 0 <= xx <= pi/2. Chris@16: bool b_negate_cos = false; Chris@16: Chris@16: if(eval_get_sign(x) < 0) Chris@16: { Chris@16: xx.negate(); Chris@16: } Chris@16: Chris@16: T n_pi, t; Chris@16: // Remove even multiples of pi. Chris@16: if(xx.compare(get_constant_pi()) > 0) Chris@16: { Chris@16: eval_divide(t, xx, get_constant_pi()); Chris@16: eval_trunc(n_pi, t); Chris@16: BOOST_MATH_INSTRUMENT_CODE(n_pi.str(0, std::ios_base::scientific)); Chris@16: eval_multiply(t, n_pi, get_constant_pi()); Chris@16: BOOST_MATH_INSTRUMENT_CODE(t.str(0, std::ios_base::scientific)); Chris@16: eval_subtract(xx, t); Chris@16: BOOST_MATH_INSTRUMENT_CODE(xx.str(0, std::ios_base::scientific)); Chris@16: Chris@16: // Adjust signs if the multiple of pi is not even. Chris@16: t = ui_type(2); Chris@16: eval_fmod(t, n_pi, t); Chris@16: const bool b_n_pi_is_even = eval_get_sign(t) == 0; Chris@16: Chris@16: if(!b_n_pi_is_even) Chris@16: { Chris@16: b_negate_cos = !b_negate_cos; Chris@16: } Chris@16: } Chris@16: Chris@16: // Reduce the argument to 0 <= xx <= pi/2. Chris@16: eval_ldexp(t, get_constant_pi(), -1); Chris@16: int com = xx.compare(t); Chris@16: if(com > 0) Chris@16: { Chris@16: eval_subtract(xx, get_constant_pi(), xx); Chris@16: b_negate_cos = !b_negate_cos; Chris@16: BOOST_MATH_INSTRUMENT_CODE(xx.str(0, std::ios_base::scientific)); Chris@16: } Chris@16: Chris@16: const bool b_zero = eval_get_sign(xx) == 0; Chris@16: const bool b_pi_half = com == 0; Chris@16: Chris@16: // Check if the reduced argument is very close to 0. Chris@16: const bool b_near_zero = xx.compare(fp_type(1e-1)) < 0; Chris@16: Chris@16: if(b_zero) Chris@16: { Chris@16: result = si_type(1); Chris@16: } Chris@16: else if(b_pi_half) Chris@16: { Chris@16: result = si_type(0); Chris@16: } Chris@16: else if(b_near_zero) Chris@16: { Chris@16: eval_multiply(t, xx, xx); Chris@16: eval_divide(t, si_type(-4)); Chris@16: n_pi = fp_type(0.5f); Chris@16: hyp0F1(result, n_pi, t); Chris@16: BOOST_MATH_INSTRUMENT_CODE(result.str(0, std::ios_base::scientific)); Chris@16: } Chris@16: else Chris@16: { Chris@16: eval_subtract(t, xx); Chris@16: eval_sin(result, t); Chris@16: } Chris@16: if(b_negate_cos) Chris@16: result.negate(); Chris@16: } Chris@16: Chris@16: template Chris@16: void eval_tan(T& result, const T& x) Chris@16: { Chris@16: BOOST_STATIC_ASSERT_MSG(number_category::value == number_kind_floating_point, "The tan function is only valid for floating point types."); Chris@16: if(&result == &x) Chris@16: { Chris@16: T temp; Chris@16: eval_tan(temp, x); Chris@16: result = temp; Chris@16: return; Chris@16: } Chris@16: T t; Chris@16: eval_sin(result, x); Chris@16: eval_cos(t, x); Chris@16: eval_divide(result, t); Chris@16: } Chris@16: Chris@16: template Chris@16: void hyp2F1(T& result, const T& a, const T& b, const T& c, const T& x) Chris@16: { Chris@16: // Compute the series representation of hyperg_2f1 taken from Chris@16: // Abramowitz and Stegun 15.1.1. Chris@16: // There are no checks on input range or parameter boundaries. Chris@16: Chris@16: typedef typename boost::multiprecision::detail::canonical::type ui_type; Chris@16: Chris@16: T x_pow_n_div_n_fact(x); Chris@16: T pochham_a (a); Chris@16: T pochham_b (b); Chris@16: T pochham_c (c); Chris@16: T ap (a); Chris@16: T bp (b); Chris@16: T cp (c); Chris@16: Chris@16: eval_multiply(result, pochham_a, pochham_b); Chris@16: eval_divide(result, pochham_c); Chris@16: eval_multiply(result, x_pow_n_div_n_fact); Chris@16: eval_add(result, ui_type(1)); Chris@16: Chris@16: T lim; Chris@16: eval_ldexp(lim, result, 1 - boost::multiprecision::detail::digits2 >::value); Chris@16: Chris@16: if(eval_get_sign(lim) < 0) Chris@16: lim.negate(); Chris@16: Chris@16: ui_type n; Chris@16: T term; 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_2f1(a, b; c; 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: Chris@16: eval_increment(ap); Chris@16: eval_multiply(pochham_a, ap); Chris@16: eval_increment(bp); Chris@16: eval_multiply(pochham_b, bp); Chris@16: eval_increment(cp); Chris@16: eval_multiply(pochham_c, cp); Chris@16: Chris@16: eval_multiply(term, pochham_a, pochham_b); Chris@16: eval_divide(term, pochham_c); Chris@16: eval_multiply(term, x_pow_n_div_n_fact); Chris@16: eval_add(result, term); Chris@16: 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("H2F1 failed to converge.")); Chris@16: } Chris@16: Chris@16: template Chris@16: void eval_asin(T& result, const T& x) Chris@16: { Chris@16: BOOST_STATIC_ASSERT_MSG(number_category::value == number_kind_floating_point, "The asin function is only valid for floating point types."); Chris@16: typedef typename boost::multiprecision::detail::canonical::type ui_type; Chris@16: typedef typename mpl::front::type fp_type; Chris@16: Chris@16: if(&result == &x) Chris@16: { Chris@16: T t(x); Chris@16: eval_asin(result, t); Chris@16: return; Chris@16: } Chris@16: Chris@16: switch(eval_fpclassify(x)) Chris@16: { Chris@16: case FP_NAN: Chris@16: case FP_INFINITE: Chris@16: if(std::numeric_limits >::has_quiet_NaN) Chris@16: result = std::numeric_limits >::quiet_NaN().backend(); Chris@16: else Chris@16: BOOST_THROW_EXCEPTION(std::domain_error("Result is undefined or complex and there is no NaN for this number type.")); Chris@16: return; Chris@16: case FP_ZERO: Chris@16: result = ui_type(0); Chris@16: return; Chris@16: default: ; Chris@16: } Chris@16: Chris@16: const bool b_neg = eval_get_sign(x) < 0; Chris@16: Chris@16: T xx(x); Chris@16: if(b_neg) Chris@16: xx.negate(); Chris@16: Chris@16: int c = xx.compare(ui_type(1)); Chris@16: if(c > 0) 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: BOOST_THROW_EXCEPTION(std::domain_error("Result is undefined or complex and there is no NaN for this number type.")); Chris@16: return; Chris@16: } Chris@16: else if(c == 0) Chris@16: { Chris@16: result = get_constant_pi(); Chris@16: eval_ldexp(result, result, -1); Chris@16: if(b_neg) Chris@16: result.negate(); Chris@16: return; Chris@16: } Chris@16: Chris@16: if(xx.compare(fp_type(1e-4)) < 0) Chris@16: { Chris@16: // http://functions.wolfram.com/ElementaryFunctions/ArcSin/26/01/01/ Chris@16: eval_multiply(xx, xx); Chris@16: T t1, t2; Chris@16: t1 = fp_type(0.5f); Chris@16: t2 = fp_type(1.5f); Chris@16: hyp2F1(result, t1, t1, t2, xx); Chris@16: eval_multiply(result, x); Chris@16: return; Chris@16: } Chris@16: else if(xx.compare(fp_type(1 - 1e-4f)) > 0) Chris@16: { Chris@16: T dx1; Chris@16: T t1, t2; Chris@16: eval_subtract(dx1, ui_type(1), xx); Chris@16: t1 = fp_type(0.5f); Chris@16: t2 = fp_type(1.5f); Chris@16: eval_ldexp(dx1, dx1, -1); Chris@16: hyp2F1(result, t1, t1, t2, dx1); Chris@16: eval_ldexp(dx1, dx1, 2); Chris@16: eval_sqrt(t1, dx1); Chris@16: eval_multiply(result, t1); Chris@16: eval_ldexp(t1, get_constant_pi(), -1); Chris@16: result.negate(); Chris@16: eval_add(result, t1); Chris@16: if(b_neg) Chris@16: result.negate(); Chris@16: return; Chris@16: } Chris@16: Chris@16: // Get initial estimate using standard math function asin. Chris@16: double dd; Chris@16: eval_convert_to(&dd, xx); Chris@16: Chris@16: result = fp_type(std::asin(dd)); Chris@16: Chris@101: unsigned current_digits = std::numeric_limits::digits - 5; Chris@101: unsigned target_precision = boost::multiprecision::detail::digits2 >::value; Chris@101: Chris@16: // Newton-Raphson iteration Chris@101: while(current_digits < target_precision) Chris@16: { Chris@16: T s, c; Chris@16: eval_sin(s, result); Chris@16: eval_cos(c, result); Chris@16: eval_subtract(s, xx); Chris@16: eval_divide(s, c); Chris@16: eval_subtract(result, s); Chris@16: Chris@101: current_digits *= 2; Chris@101: /* Chris@16: T lim; Chris@16: eval_ldexp(lim, result, 1 - boost::multiprecision::detail::digits2 >::value); Chris@16: if(eval_get_sign(s) < 0) Chris@16: s.negate(); Chris@16: if(eval_get_sign(lim) < 0) Chris@16: lim.negate(); Chris@16: if(lim.compare(s) >= 0) Chris@16: break; Chris@101: */ Chris@16: } Chris@16: if(b_neg) Chris@16: result.negate(); Chris@16: } Chris@16: Chris@16: template Chris@16: inline void eval_acos(T& result, const T& x) Chris@16: { Chris@16: BOOST_STATIC_ASSERT_MSG(number_category::value == number_kind_floating_point, "The acos function is only valid for floating point types."); Chris@16: typedef typename boost::multiprecision::detail::canonical::type ui_type; Chris@16: Chris@16: switch(eval_fpclassify(x)) Chris@16: { Chris@16: case FP_NAN: Chris@16: case FP_INFINITE: Chris@16: if(std::numeric_limits >::has_quiet_NaN) Chris@16: result = std::numeric_limits >::quiet_NaN().backend(); Chris@16: else Chris@16: BOOST_THROW_EXCEPTION(std::domain_error("Result is undefined or complex and there is no NaN for this number type.")); Chris@16: return; Chris@16: case FP_ZERO: Chris@16: result = get_constant_pi(); Chris@16: eval_ldexp(result, result, -1); // divide by two. Chris@16: return; Chris@16: } Chris@16: Chris@16: eval_abs(result, x); Chris@16: int c = result.compare(ui_type(1)); Chris@16: Chris@16: if(c > 0) 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: BOOST_THROW_EXCEPTION(std::domain_error("Result is undefined or complex and there is no NaN for this number type.")); Chris@16: return; Chris@16: } Chris@16: else if(c == 0) Chris@16: { Chris@16: if(eval_get_sign(x) < 0) Chris@16: result = get_constant_pi(); Chris@16: else Chris@16: result = ui_type(0); Chris@16: return; Chris@16: } Chris@16: Chris@16: eval_asin(result, x); Chris@16: T t; Chris@16: eval_ldexp(t, get_constant_pi(), -1); Chris@16: eval_subtract(result, t); Chris@16: result.negate(); Chris@16: } Chris@16: Chris@16: template Chris@16: void eval_atan(T& result, const T& x) Chris@16: { Chris@16: BOOST_STATIC_ASSERT_MSG(number_category::value == number_kind_floating_point, "The atan function is only valid for floating point types."); Chris@16: typedef typename boost::multiprecision::detail::canonical::type si_type; 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: result = x; Chris@16: return; Chris@16: case FP_ZERO: Chris@16: result = ui_type(0); Chris@16: return; Chris@16: case FP_INFINITE: Chris@16: if(eval_get_sign(x) < 0) Chris@16: { Chris@16: eval_ldexp(result, get_constant_pi(), -1); Chris@16: result.negate(); Chris@16: } Chris@16: else Chris@16: eval_ldexp(result, get_constant_pi(), -1); Chris@16: return; Chris@16: default: ; Chris@16: } Chris@16: Chris@16: const bool b_neg = eval_get_sign(x) < 0; Chris@16: Chris@16: T xx(x); Chris@16: if(b_neg) Chris@16: xx.negate(); Chris@16: Chris@16: if(xx.compare(fp_type(0.1)) < 0) Chris@16: { Chris@16: T t1, t2, t3; Chris@16: t1 = ui_type(1); Chris@16: t2 = fp_type(0.5f); Chris@16: t3 = fp_type(1.5f); Chris@16: eval_multiply(xx, xx); Chris@16: xx.negate(); Chris@16: hyp2F1(result, t1, t2, t3, xx); Chris@16: eval_multiply(result, x); Chris@16: return; Chris@16: } Chris@16: Chris@16: if(xx.compare(fp_type(10)) > 0) Chris@16: { Chris@16: T t1, t2, t3; Chris@16: t1 = fp_type(0.5f); Chris@16: t2 = ui_type(1u); Chris@16: t3 = fp_type(1.5f); Chris@16: eval_multiply(xx, xx); Chris@16: eval_divide(xx, si_type(-1), xx); Chris@16: hyp2F1(result, t1, t2, t3, xx); Chris@16: eval_divide(result, x); Chris@16: if(!b_neg) Chris@16: result.negate(); Chris@16: eval_ldexp(t1, get_constant_pi(), -1); Chris@16: eval_add(result, t1); Chris@16: if(b_neg) Chris@16: result.negate(); Chris@16: return; Chris@16: } Chris@16: Chris@16: Chris@16: // Get initial estimate using standard math function atan. Chris@16: fp_type d; Chris@16: eval_convert_to(&d, xx); Chris@16: result = fp_type(std::atan(d)); Chris@16: Chris@16: // Newton-Raphson iteration Chris@16: static const boost::int32_t double_digits10_minus_a_few = std::numeric_limits::digits10 - 3; Chris@16: Chris@16: T s, c, t; Chris@16: for(boost::int32_t digits = double_digits10_minus_a_few; digits <= std::numeric_limits >::digits10; digits *= 2) Chris@16: { Chris@16: eval_sin(s, result); Chris@16: eval_cos(c, result); Chris@16: eval_multiply(t, xx, c); Chris@16: eval_subtract(t, s); Chris@16: eval_multiply(s, t, c); Chris@16: eval_add(result, s); Chris@16: } Chris@16: if(b_neg) Chris@16: result.negate(); Chris@16: } Chris@16: Chris@16: template Chris@16: void eval_atan2(T& result, const T& y, const T& x) Chris@16: { Chris@16: BOOST_STATIC_ASSERT_MSG(number_category::value == number_kind_floating_point, "The atan2 function is only valid for floating point types."); Chris@16: if(&result == &y) Chris@16: { Chris@16: T temp(y); Chris@16: eval_atan2(result, temp, x); Chris@16: return; Chris@16: } Chris@16: else if(&result == &x) Chris@16: { Chris@16: T temp(x); Chris@16: eval_atan2(result, y, temp); Chris@16: return; Chris@16: } Chris@16: Chris@16: typedef typename boost::multiprecision::detail::canonical::type ui_type; Chris@16: Chris@16: switch(eval_fpclassify(y)) Chris@16: { Chris@16: case FP_NAN: Chris@16: result = y; Chris@16: return; Chris@16: case FP_ZERO: Chris@16: { Chris@16: int c = eval_get_sign(x); Chris@16: if(c < 0) Chris@16: result = get_constant_pi(); Chris@16: else if(c >= 0) Chris@16: result = ui_type(0); // Note we allow atan2(0,0) to be zero, even though it's mathematically undefined Chris@16: return; Chris@16: } Chris@16: case FP_INFINITE: Chris@16: { Chris@16: if(eval_fpclassify(x) == FP_INFINITE) 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: BOOST_THROW_EXCEPTION(std::domain_error("Result is undefined or complex and there is no NaN for this number type.")); Chris@16: } Chris@16: else Chris@16: { Chris@16: eval_ldexp(result, get_constant_pi(), -1); Chris@16: if(eval_get_sign(y) < 0) Chris@16: result.negate(); Chris@16: } Chris@16: return; Chris@16: } Chris@16: } Chris@16: Chris@16: switch(eval_fpclassify(x)) Chris@16: { Chris@16: case FP_NAN: Chris@16: result = x; Chris@16: return; Chris@16: case FP_ZERO: Chris@16: { Chris@16: eval_ldexp(result, get_constant_pi(), -1); Chris@16: if(eval_get_sign(y) < 0) Chris@16: result.negate(); Chris@16: return; Chris@16: } Chris@16: case FP_INFINITE: Chris@16: if(eval_get_sign(x) > 0) Chris@16: result = ui_type(0); Chris@16: else Chris@16: result = get_constant_pi(); Chris@16: if(eval_get_sign(y) < 0) Chris@16: result.negate(); Chris@16: return; Chris@16: } Chris@16: Chris@16: T xx; Chris@16: eval_divide(xx, y, x); Chris@16: if(eval_get_sign(xx) < 0) Chris@16: xx.negate(); Chris@16: Chris@16: eval_atan(result, xx); Chris@16: Chris@16: // Determine quadrant (sign) based on signs of x, y Chris@16: const bool y_neg = eval_get_sign(y) < 0; Chris@16: const bool x_neg = eval_get_sign(x) < 0; Chris@16: Chris@16: if(y_neg != x_neg) Chris@16: result.negate(); Chris@16: Chris@16: if(x_neg) Chris@16: { Chris@16: if(y_neg) Chris@16: eval_subtract(result, get_constant_pi()); Chris@16: else Chris@16: eval_add(result, get_constant_pi()); Chris@16: } Chris@16: } Chris@16: template Chris@16: inline typename enable_if, void>::type eval_atan2(T& result, const T& x, const A& 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 = a; Chris@16: eval_atan2(result, x, c); Chris@16: } Chris@16: Chris@16: template Chris@16: inline typename enable_if, void>::type eval_atan2(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_atan2(result, c, a); Chris@16: } Chris@16: