void eval_log(T& result, const T& arg) { BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The log function is only valid for floating point types."); // // We use a variation of http://dlmf.nist.gov/4.45#i // using frexp to reduce the argument to x * 2^n, // then let y = x - 1 and compute: // log(x) = log(2) * n + log1p(1 + y) // typedef typename boost::multiprecision::detail::canonical<int, T>::type si_type; typedef typename boost::multiprecision::detail::canonical<unsigned, T>::type ui_type; typedef typename T::exponent_type exp_type; typedef typename boost::multiprecision::detail::canonical<exp_type, T>::type canonical_exp_type; typedef typename mpl::front<typename T::float_types>::type fp_type; exp_type e; T t; eval_frexp(t, arg, &e); bool alternate = false; if(t.compare(fp_type(2) / fp_type(3)) <= 0) { alternate = true; eval_ldexp(t, t, 1); --e; } eval_multiply(result, get_constant_ln2<T>(), canonical_exp_type(e)); INSTRUMENT_BACKEND(result); eval_subtract(t, ui_type(1)); /* -0.3 <= t <= 0.3 */ if(!alternate) t.negate(); /* 0 <= t <= 0.33333 */ T pow = t; T lim; T t2; if(alternate) eval_add(result, t); else eval_subtract(result, t); eval_multiply(lim, result, std::numeric_limits<number<T, et_on> >::epsilon().backend()); if(eval_get_sign(lim) < 0) lim.negate(); INSTRUMENT_BACKEND(lim); ui_type k = 1; do { ++k; eval_multiply(pow, t); eval_divide(t2, pow, k); INSTRUMENT_BACKEND(t2); if(alternate && ((k & 1) != 0)) eval_add(result, t2); else eval_subtract(result, t2); INSTRUMENT_BACKEND(result); }while(lim.compare(t2) < 0); }
void sinhcosh(const T& x, T* p_sinh, T* p_cosh) { typedef typename boost::multiprecision::detail::canonical<unsigned, T>::type ui_type; typedef typename mpl::front<typename T::float_types>::type fp_type; switch(eval_fpclassify(x)) { case FP_NAN: case FP_INFINITE: if(p_sinh) *p_sinh = x; if(p_cosh) { *p_cosh = x; if(eval_get_sign(x) < 0) p_cosh->negate(); } return; case FP_ZERO: if(p_sinh) *p_sinh = x; if(p_cosh) *p_cosh = ui_type(1); return; default: ; } bool small_sinh = eval_get_sign(x) < 0 ? x.compare(fp_type(-0.5)) > 0 : x.compare(fp_type(0.5)) < 0; if(p_cosh || !small_sinh) { T e_px, e_mx; eval_exp(e_px, x); eval_divide(e_mx, ui_type(1), e_px); if(p_sinh) { if(small_sinh) { small_sinh_series(x, *p_sinh); } else { eval_subtract(*p_sinh, e_px, e_mx); eval_ldexp(*p_sinh, *p_sinh, -1); } } if(p_cosh) { eval_add(*p_cosh, e_px, e_mx); eval_ldexp(*p_cosh, *p_cosh, -1); } } else { small_sinh_series(x, *p_sinh); } }
inline void eval_acos(T& result, const T& x) { BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The acos function is only valid for floating point types."); typedef typename boost::multiprecision::detail::canonical<boost::uint32_t, T>::type ui_type; switch(eval_fpclassify(x)) { case FP_NAN: case FP_INFINITE: if(std::numeric_limits<number<T, et_on> >::has_quiet_NaN) { result = std::numeric_limits<number<T, et_on> >::quiet_NaN().backend(); errno = EDOM; } else BOOST_THROW_EXCEPTION(std::domain_error("Result is undefined or complex and there is no NaN for this number type.")); return; case FP_ZERO: result = get_constant_pi<T>(); eval_ldexp(result, result, -1); // divide by two. return; } eval_abs(result, x); int c = result.compare(ui_type(1)); if(c > 0) { if(std::numeric_limits<number<T, et_on> >::has_quiet_NaN) { result = std::numeric_limits<number<T, et_on> >::quiet_NaN().backend(); errno = EDOM; } else BOOST_THROW_EXCEPTION(std::domain_error("Result is undefined or complex and there is no NaN for this number type.")); return; } else if(c == 0) { if(eval_get_sign(x) < 0) result = get_constant_pi<T>(); else result = ui_type(0); return; } eval_asin(result, x); T t; eval_ldexp(t, get_constant_pi<T>(), -1); eval_subtract(result, t); result.negate(); }
inline void eval_pow(T& result, const T& x, const T& a) { BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The pow function is only valid for floating point types."); typedef typename boost::multiprecision::detail::canonical<int, T>::type si_type; typedef typename boost::multiprecision::detail::canonical<unsigned, T>::type ui_type; typedef typename T::exponent_type exp_type; typedef typename boost::multiprecision::detail::canonical<exp_type, T>::type canonical_exp_type; typedef typename mpl::front<typename T::float_types>::type fp_type; if((&result == &x) || (&result == &a)) { T t; eval_pow(t, x, a); result = t; return; } if(a.compare(si_type(1)) == 0) { result = x; return; } int type = eval_fpclassify(x); switch(type) { case FP_INFINITE: result = x; return; case FP_ZERO: result = si_type(1); return; case FP_NAN: result = x; return; default: ; } if(eval_get_sign(a) == 0) { result = si_type(1); return; } if(a.compare(si_type(-1)) < 0) { T t, da; t = a; t.negate(); eval_pow(da, x, t); eval_divide(result, si_type(1), da); return; } bool bo_a_isint = false; typename boost::multiprecision::detail::canonical<boost::intmax_t, T>::type an; T fa; try { eval_convert_to(&an, a); if(a.compare(an) == 0) { detail::pow_imp(result, x, an, mpl::true_()); return; } } catch(const std::exception&) { // conversion failed, just fall through, value is not an integer. an = (std::numeric_limits<boost::intmax_t>::max)(); } if((eval_get_sign(x) < 0) && !bo_a_isint) { result = std::numeric_limits<number<T, et_on> >::quiet_NaN().backend(); } T t, da; eval_subtract(da, a, an); if((x.compare(fp_type(0.5)) >= 0) && (x.compare(fp_type(0.9)) < 0)) { if(a.compare(fp_type(1e-5f)) <= 0) { // Series expansion for small a. eval_log(t, x); eval_multiply(t, a); hyp0F0(result, t); return; } else { // Series expansion for moderately sized x. Note that for large power of a, // the power of the integer part of a is calculated using the pown function. if(an) { da.negate(); t = si_type(1); eval_subtract(t, x); hyp1F0(result, da, t); detail::pow_imp(t, x, an, mpl::true_()); eval_multiply(result, t); } else { da = a; da.negate(); t = si_type(1); eval_subtract(t, x); hyp1F0(result, da, t); } } } else { // Series expansion for pow(x, a). Note that for large power of a, the power // of the integer part of a is calculated using the pown function. if(an) { eval_log(t, x); eval_multiply(t, da); eval_exp(result, t); detail::pow_imp(t, x, an, mpl::true_()); eval_multiply(result, t); } else { eval_log(t, x); eval_multiply(t, a); eval_exp(result, t); } } }
void eval_exp(T& result, const T& x) { BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The exp function is only valid for floating point types."); if(&x == &result) { T temp; eval_exp(temp, x); result = temp; return; } typedef typename boost::multiprecision::detail::canonical<unsigned, T>::type ui_type; typedef typename boost::multiprecision::detail::canonical<int, T>::type si_type; typedef typename T::exponent_type exp_type; typedef typename boost::multiprecision::detail::canonical<exp_type, T>::type canonical_exp_type; typedef typename boost::multiprecision::detail::canonical<float, T>::type float_type; // Handle special arguments. int type = eval_fpclassify(x); bool isneg = eval_get_sign(x) < 0; if(type == FP_NAN) { result = std::numeric_limits<number<T, et_on> >::quiet_NaN().backend(); return; } else if(type == FP_INFINITE) { result = x; if(isneg) result = ui_type(0u); else result = x; return; } else if(type == FP_ZERO) { result = ui_type(1); return; } // Get local copy of argument and force it to be positive. T xx = x; T exp_series; if(isneg) xx.negate(); // Check the range of the argument. static const canonical_exp_type maximum_arg_for_exp = std::numeric_limits<number<T, et_on> >::max_exponent == 0 ? (std::numeric_limits<long>::max)() : std::numeric_limits<number<T, et_on> >::max_exponent; if(xx.compare(maximum_arg_for_exp) >= 0) { // Overflow / underflow if(isneg) result = ui_type(0); else result = std::numeric_limits<number<T, et_on> >::has_infinity ? std::numeric_limits<number<T, et_on> >::infinity().backend() : (std::numeric_limits<number<T, et_on> >::max)().backend(); return; } if(xx.compare(si_type(1)) <= 0) { // // Use series for exp(x) - 1: // T lim = std::numeric_limits<number<T, et_on> >::epsilon().backend(); unsigned k = 2; exp_series = xx; result = si_type(1); if(isneg) eval_subtract(result, exp_series); else eval_add(result, exp_series); eval_multiply(exp_series, xx); eval_divide(exp_series, ui_type(k)); eval_add(result, exp_series); while(exp_series.compare(lim) > 0) { ++k; eval_multiply(exp_series, xx); eval_divide(exp_series, ui_type(k)); if(isneg && (k&1)) eval_subtract(result, exp_series); else eval_add(result, exp_series); } return; } // Check for pure-integer arguments which can be either signed or unsigned. typename boost::multiprecision::detail::canonical<boost::intmax_t, T>::type ll; eval_trunc(exp_series, x); eval_convert_to(&ll, exp_series); if(x.compare(ll) == 0) { detail::pow_imp(result, get_constant_e<T>(), ll, mpl::true_()); return; } // The algorithm for exp has been taken from MPFUN. // exp(t) = [ (1 + r + r^2/2! + r^3/3! + r^4/4! ...)^p2 ] * 2^n // where p2 is a power of 2 such as 2048, r = t_prime / p2, and // t_prime = t - n*ln2, with n chosen to minimize the absolute // value of t_prime. In the resulting Taylor series, which is // implemented as a hypergeometric function, |r| is bounded by // ln2 / p2. For small arguments, no scaling is done. // Compute the exponential series of the (possibly) scaled argument. eval_divide(result, xx, get_constant_ln2<T>()); exp_type n; eval_convert_to(&n, result); // The scaling is 2^11 = 2048. static const si_type p2 = static_cast<si_type>(si_type(1) << 11); eval_multiply(exp_series, get_constant_ln2<T>(), static_cast<canonical_exp_type>(n)); eval_subtract(exp_series, xx); eval_divide(exp_series, p2); exp_series.negate(); hyp0F0(result, exp_series); detail::pow_imp(exp_series, result, p2, mpl::true_()); result = ui_type(1); eval_ldexp(result, result, n); eval_multiply(exp_series, result); if(isneg) eval_divide(result, ui_type(1), exp_series); else result = exp_series; }
inline void eval_pow(T& result, const T& x, const T& a) { BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The pow function is only valid for floating point types."); typedef typename boost::multiprecision::detail::canonical<int, T>::type si_type; typedef typename mpl::front<typename T::float_types>::type fp_type; if((&result == &x) || (&result == &a)) { T t; eval_pow(t, x, a); result = t; return; } if(a.compare(si_type(1)) == 0) { result = x; return; } int type = eval_fpclassify(x); switch(type) { case FP_INFINITE: result = x; return; case FP_ZERO: switch(eval_fpclassify(a)) { case FP_ZERO: result = si_type(1); break; case FP_NAN: result = a; break; default: result = x; break; } return; case FP_NAN: result = x; return; default: ; } int s = eval_get_sign(a); if(s == 0) { result = si_type(1); return; } if(s < 0) { T t, da; t = a; t.negate(); eval_pow(da, x, t); eval_divide(result, si_type(1), da); return; } typename boost::multiprecision::detail::canonical<boost::intmax_t, T>::type an; T fa; #ifndef BOOST_NO_EXCEPTIONS try { #endif eval_convert_to(&an, a); if(a.compare(an) == 0) { detail::pow_imp(result, x, an, mpl::true_()); return; } #ifndef BOOST_NO_EXCEPTIONS } catch(const std::exception&) { // conversion failed, just fall through, value is not an integer. an = (std::numeric_limits<boost::intmax_t>::max)(); } #endif if((eval_get_sign(x) < 0)) { typename boost::multiprecision::detail::canonical<boost::uintmax_t, T>::type aun; #ifndef BOOST_NO_EXCEPTIONS try { #endif eval_convert_to(&aun, a); if(a.compare(aun) == 0) { fa = x; fa.negate(); eval_pow(result, fa, a); if(aun & 1u) result.negate(); return; } #ifndef BOOST_NO_EXCEPTIONS } catch(const std::exception&) { // conversion failed, just fall through, value is not an integer. } #endif if(std::numeric_limits<number<T, et_on> >::has_quiet_NaN) result = std::numeric_limits<number<T, et_on> >::quiet_NaN().backend(); else { BOOST_THROW_EXCEPTION(std::domain_error("Result of pow is undefined or non-real and there is no NaN for this number type.")); } return; } T t, da; eval_subtract(da, a, an); if((x.compare(fp_type(0.5)) >= 0) && (x.compare(fp_type(0.9)) < 0)) { if(a.compare(fp_type(1e-5f)) <= 0) { // Series expansion for small a. eval_log(t, x); eval_multiply(t, a); hyp0F0(result, t); return; } else { // Series expansion for moderately sized x. Note that for large power of a, // the power of the integer part of a is calculated using the pown function. if(an) { da.negate(); t = si_type(1); eval_subtract(t, x); hyp1F0(result, da, t); detail::pow_imp(t, x, an, mpl::true_()); eval_multiply(result, t); } else { da = a; da.negate(); t = si_type(1); eval_subtract(t, x); hyp1F0(result, da, t); } } } else { // Series expansion for pow(x, a). Note that for large power of a, the power // of the integer part of a is calculated using the pown function. if(an) { eval_log(t, x); eval_multiply(t, da); eval_exp(result, t); detail::pow_imp(t, x, an, mpl::true_()); eval_multiply(result, t); } else { eval_log(t, x); eval_multiply(t, a); eval_exp(result, t); } } }
void eval_sin(T& result, const T& x) { BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The sin function is only valid for floating point types."); if(&result == &x) { T temp; eval_sin(temp, x); result = temp; return; } typedef typename boost::multiprecision::detail::canonical<boost::int32_t, T>::type si_type; typedef typename boost::multiprecision::detail::canonical<boost::uint32_t, T>::type ui_type; typedef typename mpl::front<typename T::float_types>::type fp_type; switch(eval_fpclassify(x)) { case FP_INFINITE: case FP_NAN: if(std::numeric_limits<number<T, et_on> >::has_quiet_NaN) result = std::numeric_limits<number<T, et_on> >::quiet_NaN().backend(); else BOOST_THROW_EXCEPTION(std::domain_error("Result is undefined or complex and there is no NaN for this number type.")); return; case FP_ZERO: result = ui_type(0); return; default: ; } // Local copy of the argument T xx = x; // Analyze and prepare the phase of the argument. // Make a local, positive copy of the argument, xx. // The argument xx will be reduced to 0 <= xx <= pi/2. bool b_negate_sin = false; if(eval_get_sign(x) < 0) { xx.negate(); b_negate_sin = !b_negate_sin; } T n_pi, t; // Remove even multiples of pi. if(xx.compare(get_constant_pi<T>()) > 0) { eval_divide(n_pi, xx, get_constant_pi<T>()); eval_trunc(n_pi, n_pi); t = ui_type(2); eval_fmod(t, n_pi, t); const bool b_n_pi_is_even = eval_get_sign(t) == 0; eval_multiply(n_pi, get_constant_pi<T>()); eval_subtract(xx, n_pi); BOOST_MATH_INSTRUMENT_CODE(xx.str(0, std::ios_base::scientific)); BOOST_MATH_INSTRUMENT_CODE(n_pi.str(0, std::ios_base::scientific)); // Adjust signs if the multiple of pi is not even. if(!b_n_pi_is_even) { b_negate_sin = !b_negate_sin; } } // Reduce the argument to 0 <= xx <= pi/2. eval_ldexp(t, get_constant_pi<T>(), -1); if(xx.compare(t) > 0) { eval_subtract(xx, get_constant_pi<T>(), xx); BOOST_MATH_INSTRUMENT_CODE(xx.str(0, std::ios_base::scientific)); } eval_subtract(t, xx); const bool b_zero = eval_get_sign(xx) == 0; const bool b_pi_half = eval_get_sign(t) == 0; // Check if the reduced argument is very close to 0 or pi/2. const bool b_near_zero = xx.compare(fp_type(1e-1)) < 0; const bool b_near_pi_half = t.compare(fp_type(1e-1)) < 0;; if(b_zero) { result = ui_type(0); } else if(b_pi_half) { result = ui_type(1); } else if(b_near_zero) { eval_multiply(t, xx, xx); eval_divide(t, si_type(-4)); T t2; t2 = fp_type(1.5); hyp0F1(result, t2, t); BOOST_MATH_INSTRUMENT_CODE(result.str(0, std::ios_base::scientific)); eval_multiply(result, xx); } else if(b_near_pi_half) { eval_multiply(t, t); eval_divide(t, si_type(-4)); T t2; t2 = fp_type(0.5); hyp0F1(result, t2, t); BOOST_MATH_INSTRUMENT_CODE(result.str(0, std::ios_base::scientific)); } else { // Scale to a small argument for an efficient Taylor series, // implemented as a hypergeometric function. Use a standard // divide by three identity a certain number of times. // Here we use division by 3^9 --> (19683 = 3^9). static const si_type n_scale = 9; static const si_type n_three_pow_scale = static_cast<si_type>(19683L); eval_divide(xx, n_three_pow_scale); // Now with small arguments, we are ready for a series expansion. eval_multiply(t, xx, xx); eval_divide(t, si_type(-4)); T t2; t2 = fp_type(1.5); hyp0F1(result, t2, t); BOOST_MATH_INSTRUMENT_CODE(result.str(0, std::ios_base::scientific)); eval_multiply(result, xx); // Convert back using multiple angle identity. for(boost::int32_t k = static_cast<boost::int32_t>(0); k < n_scale; k++) { // Rescale the cosine value using the multiple angle identity. eval_multiply(t2, result, ui_type(3)); eval_multiply(t, result, result); eval_multiply(t, result); eval_multiply(t, ui_type(4)); eval_subtract(result, t2, t); } } if(b_negate_sin) result.negate(); }
void eval_atan2(T& result, const T& y, const T& x) { BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The atan2 function is only valid for floating point types."); if(&result == &y) { T temp(y); eval_atan2(result, temp, x); return; } else if(&result == &x) { T temp(x); eval_atan2(result, y, temp); return; } typedef typename boost::multiprecision::detail::canonical<boost::uint32_t, T>::type ui_type; switch(eval_fpclassify(y)) { case FP_NAN: result = y; return; case FP_ZERO: { int c = eval_get_sign(x); if(c < 0) result = get_constant_pi<T>(); else if(c >= 0) result = ui_type(0); // Note we allow atan2(0,0) to be zero, even though it's mathematically undefined return; } case FP_INFINITE: { if(eval_fpclassify(x) == FP_INFINITE) { if(std::numeric_limits<number<T, et_on> >::has_quiet_NaN) result = std::numeric_limits<number<T, et_on> >::quiet_NaN().backend(); else BOOST_THROW_EXCEPTION(std::domain_error("Result is undefined or complex and there is no NaN for this number type.")); } else { eval_ldexp(result, get_constant_pi<T>(), -1); if(eval_get_sign(y) < 0) result.negate(); } return; } } switch(eval_fpclassify(x)) { case FP_NAN: result = x; return; case FP_ZERO: { eval_ldexp(result, get_constant_pi<T>(), -1); if(eval_get_sign(y) < 0) result.negate(); return; } case FP_INFINITE: if(eval_get_sign(x) > 0) result = ui_type(0); else result = get_constant_pi<T>(); if(eval_get_sign(y) < 0) result.negate(); return; } T xx; eval_divide(xx, y, x); if(eval_get_sign(xx) < 0) xx.negate(); eval_atan(result, xx); // Determine quadrant (sign) based on signs of x, y const bool y_neg = eval_get_sign(y) < 0; const bool x_neg = eval_get_sign(x) < 0; if(y_neg != x_neg) result.negate(); if(x_neg) { if(y_neg) eval_subtract(result, get_constant_pi<T>()); else eval_add(result, get_constant_pi<T>()); } }
void eval_atan(T& result, const T& x) { BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The atan function is only valid for floating point types."); typedef typename boost::multiprecision::detail::canonical<boost::int32_t, T>::type si_type; typedef typename boost::multiprecision::detail::canonical<boost::uint32_t, T>::type ui_type; typedef typename mpl::front<typename T::float_types>::type fp_type; switch(eval_fpclassify(x)) { case FP_NAN: result = x; return; case FP_ZERO: result = ui_type(0); return; case FP_INFINITE: if(eval_get_sign(x) < 0) { eval_ldexp(result, get_constant_pi<T>(), -1); result.negate(); } else eval_ldexp(result, get_constant_pi<T>(), -1); return; default: ; } const bool b_neg = eval_get_sign(x) < 0; T xx(x); if(b_neg) xx.negate(); if(xx.compare(fp_type(0.1)) < 0) { T t1, t2, t3; t1 = ui_type(1); t2 = fp_type(0.5f); t3 = fp_type(1.5f); eval_multiply(xx, xx); xx.negate(); hyp2F1(result, t1, t2, t3, xx); eval_multiply(result, x); return; } if(xx.compare(fp_type(10)) > 0) { T t1, t2, t3; t1 = fp_type(0.5f); t2 = ui_type(1u); t3 = fp_type(1.5f); eval_multiply(xx, xx); eval_divide(xx, si_type(-1), xx); hyp2F1(result, t1, t2, t3, xx); eval_divide(result, x); if(!b_neg) result.negate(); eval_ldexp(t1, get_constant_pi<T>(), -1); eval_add(result, t1); if(b_neg) result.negate(); return; } // Get initial estimate using standard math function atan. fp_type d; eval_convert_to(&d, xx); result = fp_type(std::atan(d)); // Newton-Raphson iteration static const boost::int32_t double_digits10_minus_a_few = std::numeric_limits<double>::digits10 - 3; T s, c, t; for(boost::int32_t digits = double_digits10_minus_a_few; digits <= std::numeric_limits<number<T, et_on> >::digits10; digits *= 2) { eval_sin(s, result); eval_cos(c, result); eval_multiply(t, xx, c); eval_subtract(t, s); eval_multiply(s, t, c); eval_add(result, s); } if(b_neg) result.negate(); }
void eval_asin(T& result, const T& x) { BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The asin function is only valid for floating point types."); typedef typename boost::multiprecision::detail::canonical<boost::uint32_t, T>::type ui_type; typedef typename mpl::front<typename T::float_types>::type fp_type; if(&result == &x) { T t(x); eval_asin(result, t); return; } switch(eval_fpclassify(x)) { case FP_NAN: case FP_INFINITE: if(std::numeric_limits<number<T, et_on> >::has_quiet_NaN) result = std::numeric_limits<number<T, et_on> >::quiet_NaN().backend(); else BOOST_THROW_EXCEPTION(std::domain_error("Result is undefined or complex and there is no NaN for this number type.")); return; case FP_ZERO: result = ui_type(0); return; default: ; } const bool b_neg = eval_get_sign(x) < 0; T xx(x); if(b_neg) xx.negate(); int c = xx.compare(ui_type(1)); if(c > 0) { if(std::numeric_limits<number<T, et_on> >::has_quiet_NaN) result = std::numeric_limits<number<T, et_on> >::quiet_NaN().backend(); else BOOST_THROW_EXCEPTION(std::domain_error("Result is undefined or complex and there is no NaN for this number type.")); return; } else if(c == 0) { result = get_constant_pi<T>(); eval_ldexp(result, result, -1); if(b_neg) result.negate(); return; } if(xx.compare(fp_type(1e-4)) < 0) { // http://functions.wolfram.com/ElementaryFunctions/ArcSin/26/01/01/ eval_multiply(xx, xx); T t1, t2; t1 = fp_type(0.5f); t2 = fp_type(1.5f); hyp2F1(result, t1, t1, t2, xx); eval_multiply(result, x); return; } else if(xx.compare(fp_type(1 - 1e-4f)) > 0) { T dx1; T t1, t2; eval_subtract(dx1, ui_type(1), xx); t1 = fp_type(0.5f); t2 = fp_type(1.5f); eval_ldexp(dx1, dx1, -1); hyp2F1(result, t1, t1, t2, dx1); eval_ldexp(dx1, dx1, 2); eval_sqrt(t1, dx1); eval_multiply(result, t1); eval_ldexp(t1, get_constant_pi<T>(), -1); result.negate(); eval_add(result, t1); if(b_neg) result.negate(); return; } #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS typedef typename boost::multiprecision::detail::canonical<long double, T>::type guess_type; #else typedef fp_type guess_type; #endif // Get initial estimate using standard math function asin. guess_type dd; eval_convert_to(&dd, xx); result = (guess_type)(std::asin(dd)); unsigned current_digits = std::numeric_limits<guess_type>::digits - 5; unsigned target_precision = boost::multiprecision::detail::digits2<number<T, et_on> >::value; // Newton-Raphson iteration while(current_digits < target_precision) { T sine, cosine; eval_sin(sine, result); eval_cos(cosine, result); eval_subtract(sine, xx); eval_divide(sine, cosine); eval_subtract(result, sine); current_digits *= 2; /* T lim; eval_ldexp(lim, result, 1 - boost::multiprecision::detail::digits2<number<T, et_on> >::value); if(eval_get_sign(s) < 0) s.negate(); if(eval_get_sign(lim) < 0) lim.negate(); if(lim.compare(s) >= 0) break; */ } if(b_neg) result.negate(); }
void eval_cos(T& result, const T& x) { BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The cos function is only valid for floating point types."); if(&result == &x) { T temp; eval_cos(temp, x); result = temp; return; } typedef typename boost::multiprecision::detail::canonical<boost::int32_t, T>::type si_type; typedef typename boost::multiprecision::detail::canonical<boost::uint32_t, T>::type ui_type; typedef typename mpl::front<typename T::float_types>::type fp_type; switch(eval_fpclassify(x)) { case FP_INFINITE: case FP_NAN: if(std::numeric_limits<number<T, et_on> >::has_quiet_NaN) result = std::numeric_limits<number<T, et_on> >::quiet_NaN().backend(); else BOOST_THROW_EXCEPTION(std::domain_error("Result is undefined or complex and there is no NaN for this number type.")); return; case FP_ZERO: result = ui_type(1); return; default: ; } // Local copy of the argument T xx = x; // Analyze and prepare the phase of the argument. // Make a local, positive copy of the argument, xx. // The argument xx will be reduced to 0 <= xx <= pi/2. bool b_negate_cos = false; if(eval_get_sign(x) < 0) { xx.negate(); } T n_pi, t; // Remove even multiples of pi. if(xx.compare(get_constant_pi<T>()) > 0) { eval_divide(t, xx, get_constant_pi<T>()); eval_trunc(n_pi, t); BOOST_MATH_INSTRUMENT_CODE(n_pi.str(0, std::ios_base::scientific)); eval_multiply(t, n_pi, get_constant_pi<T>()); BOOST_MATH_INSTRUMENT_CODE(t.str(0, std::ios_base::scientific)); eval_subtract(xx, t); BOOST_MATH_INSTRUMENT_CODE(xx.str(0, std::ios_base::scientific)); // Adjust signs if the multiple of pi is not even. t = ui_type(2); eval_fmod(t, n_pi, t); const bool b_n_pi_is_even = eval_get_sign(t) == 0; if(!b_n_pi_is_even) { b_negate_cos = !b_negate_cos; } } // Reduce the argument to 0 <= xx <= pi/2. eval_ldexp(t, get_constant_pi<T>(), -1); int com = xx.compare(t); if(com > 0) { eval_subtract(xx, get_constant_pi<T>(), xx); b_negate_cos = !b_negate_cos; BOOST_MATH_INSTRUMENT_CODE(xx.str(0, std::ios_base::scientific)); } const bool b_zero = eval_get_sign(xx) == 0; const bool b_pi_half = com == 0; // Check if the reduced argument is very close to 0. const bool b_near_zero = xx.compare(fp_type(1e-1)) < 0; if(b_zero) { result = si_type(1); } else if(b_pi_half) { result = si_type(0); } else if(b_near_zero) { eval_multiply(t, xx, xx); eval_divide(t, si_type(-4)); n_pi = fp_type(0.5f); hyp0F1(result, n_pi, t); BOOST_MATH_INSTRUMENT_CODE(result.str(0, std::ios_base::scientific)); } else { eval_subtract(t, xx); eval_sin(result, t); } if(b_negate_cos) result.negate(); }
void calc_pi(T& result, unsigned digits) { typedef typename mpl::front<typename T::unsigned_types>::type ui_type; typedef typename mpl::front<typename T::float_types>::type real_type; // // 1100 digits in string form: // const char* string_val = "3." "1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679" "8214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196" "4428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273" "7245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094" "3305727036575959195309218611738193261179310511854807446237996274956735188575272489122793818301194912" "9833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132" "0005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235" "4201995611212902196086403441815981362977477130996051870721134999999837297804995105973173281609631859" "5024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303" "5982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989" "3809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913152"; // // Check if we can just construct from string: // if(digits < 3640) // 3640 binary digits ~ 1100 decimal digits { result = string_val; return; } T a; a = ui_type(1); T b; T A(a); T B; B = real_type(0.5f); T D; D = real_type(0.25f); T lim; lim = ui_type(1); eval_ldexp(lim, lim, -(int)digits); // // This algorithm is from: // Schonhage, A., Grotefeld, A. F. W., and Vetter, E. Fast Algorithms: A Multitape Turing // Machine Implementation. BI Wissenschaftverlag, 1994. // Also described in MPFR's algorithm guide: http://www.mpfr.org/algorithms.pdf. // // Let: // a[0] = A[0] = 1 // B[0] = 1/2 // D[0] = 1/4 // Then: // S[k+1] = (A[k]+B[k]) / 4 // b[k] = sqrt(B[k]) // a[k+1] = a[k]^2 // B[k+1] = 2(A[k+1]-S[k+1]) // D[k+1] = D[k] - 2^k(A[k+1]-B[k+1]) // Stop when |A[k]-B[k]| <= 2^(k-p) // and PI = B[k]/D[k] unsigned k = 1; do { eval_add(result, A, B); eval_ldexp(result, result, -2); eval_sqrt(b, B); eval_add(a, b); eval_ldexp(a, a, -1); eval_multiply(A, a, a); eval_subtract(B, A, result); eval_ldexp(B, B, 1); eval_subtract(result, A, B); bool neg = eval_get_sign(result) < 0; if(neg) result.negate(); if(result.compare(lim) <= 0) break; if(neg) result.negate(); eval_ldexp(result, result, k - 1); eval_subtract(D, result); ++k; eval_ldexp(lim, lim, 1); } while(true); eval_divide(result, B, D); }
void eval_atan2(T& result, const T& y, const T& x) { BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The atan2 function is only valid for floating point types."); if(&result == &y) { T temp(y); eval_atan2(result, temp, x); return; } else if(&result == &x) { T temp(x); eval_atan2(result, y, temp); return; } typedef typename boost::multiprecision::detail::canonical<boost::uint32_t, T>::type ui_type; switch(eval_fpclassify(y)) { case FP_NAN: result = y; errno = EDOM; return; case FP_ZERO: { if(eval_signbit(x)) { result = get_constant_pi<T>(); if(eval_signbit(y)) result.negate(); } else { result = y; // Note we allow atan2(0,0) to be +-zero, even though it's mathematically undefined } return; } case FP_INFINITE: { if(eval_fpclassify(x) == FP_INFINITE) { if(eval_signbit(x)) { // 3Pi/4 eval_ldexp(result, get_constant_pi<T>(), -2); eval_subtract(result, get_constant_pi<T>()); if(eval_get_sign(y) >= 0) result.negate(); } else { // Pi/4 eval_ldexp(result, get_constant_pi<T>(), -2); if(eval_get_sign(y) < 0) result.negate(); } } else { eval_ldexp(result, get_constant_pi<T>(), -1); if(eval_get_sign(y) < 0) result.negate(); } return; } } switch(eval_fpclassify(x)) { case FP_NAN: result = x; errno = EDOM; return; case FP_ZERO: { eval_ldexp(result, get_constant_pi<T>(), -1); if(eval_get_sign(y) < 0) result.negate(); return; } case FP_INFINITE: if(eval_get_sign(x) > 0) result = ui_type(0); else result = get_constant_pi<T>(); if(eval_get_sign(y) < 0) result.negate(); return; } T xx; eval_divide(xx, y, x); if(eval_get_sign(xx) < 0) xx.negate(); eval_atan(result, xx); // Determine quadrant (sign) based on signs of x, y const bool y_neg = eval_get_sign(y) < 0; const bool x_neg = eval_get_sign(x) < 0; if(y_neg != x_neg) result.negate(); if(x_neg) { if(y_neg) eval_subtract(result, get_constant_pi<T>()); else eval_add(result, get_constant_pi<T>()); } }
void eval_atan(T& result, const T& x) { BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The atan function is only valid for floating point types."); typedef typename boost::multiprecision::detail::canonical<boost::int32_t, T>::type si_type; typedef typename boost::multiprecision::detail::canonical<boost::uint32_t, T>::type ui_type; typedef typename mpl::front<typename T::float_types>::type fp_type; switch(eval_fpclassify(x)) { case FP_NAN: result = x; errno = EDOM; return; case FP_ZERO: result = x; return; case FP_INFINITE: if(eval_get_sign(x) < 0) { eval_ldexp(result, get_constant_pi<T>(), -1); result.negate(); } else eval_ldexp(result, get_constant_pi<T>(), -1); return; default: ; } const bool b_neg = eval_get_sign(x) < 0; T xx(x); if(b_neg) xx.negate(); if(xx.compare(fp_type(0.1)) < 0) { T t1, t2, t3; t1 = ui_type(1); t2 = fp_type(0.5f); t3 = fp_type(1.5f); eval_multiply(xx, xx); xx.negate(); hyp2F1(result, t1, t2, t3, xx); eval_multiply(result, x); return; } if(xx.compare(fp_type(10)) > 0) { T t1, t2, t3; t1 = fp_type(0.5f); t2 = ui_type(1u); t3 = fp_type(1.5f); eval_multiply(xx, xx); eval_divide(xx, si_type(-1), xx); hyp2F1(result, t1, t2, t3, xx); eval_divide(result, x); if(!b_neg) result.negate(); eval_ldexp(t1, get_constant_pi<T>(), -1); eval_add(result, t1); if(b_neg) result.negate(); return; } // Get initial estimate using standard math function atan. fp_type d; eval_convert_to(&d, xx); result = fp_type(std::atan(d)); // Newton-Raphson iteration, we should double our precision with each iteration, // in practice this seems to not quite work in all cases... so terminate when we // have at least 2/3 of the digits correct on the assumption that the correction // we've just added will finish the job... boost::intmax_t current_precision = eval_ilogb(result); boost::intmax_t target_precision = current_precision - 1 - (std::numeric_limits<number<T> >::digits * 2) / 3; T s, c, t; while(current_precision > target_precision) { eval_sin(s, result); eval_cos(c, result); eval_multiply(t, xx, c); eval_subtract(t, s); eval_multiply(s, t, c); eval_add(result, s); current_precision = eval_ilogb(s); if(current_precision <= (std::numeric_limits<typename T::exponent_type>::min)() + 1) break; } if(b_neg) result.negate(); }
void eval_asin(T& result, const T& x) { BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The asin function is only valid for floating point types."); typedef typename boost::multiprecision::detail::canonical<boost::uint32_t, T>::type ui_type; typedef typename mpl::front<typename T::float_types>::type fp_type; if(&result == &x) { T t(x); eval_asin(result, t); return; } switch(eval_fpclassify(x)) { case FP_NAN: case FP_INFINITE: if(std::numeric_limits<number<T, et_on> >::has_quiet_NaN) { result = std::numeric_limits<number<T, et_on> >::quiet_NaN().backend(); errno = EDOM; } else BOOST_THROW_EXCEPTION(std::domain_error("Result is undefined or complex and there is no NaN for this number type.")); return; case FP_ZERO: result = x; return; default: ; } const bool b_neg = eval_get_sign(x) < 0; T xx(x); if(b_neg) xx.negate(); int c = xx.compare(ui_type(1)); if(c > 0) { if(std::numeric_limits<number<T, et_on> >::has_quiet_NaN) { result = std::numeric_limits<number<T, et_on> >::quiet_NaN().backend(); errno = EDOM; } else BOOST_THROW_EXCEPTION(std::domain_error("Result is undefined or complex and there is no NaN for this number type.")); return; } else if(c == 0) { result = get_constant_pi<T>(); eval_ldexp(result, result, -1); if(b_neg) result.negate(); return; } if(xx.compare(fp_type(1e-4)) < 0) { // http://functions.wolfram.com/ElementaryFunctions/ArcSin/26/01/01/ eval_multiply(xx, xx); T t1, t2; t1 = fp_type(0.5f); t2 = fp_type(1.5f); hyp2F1(result, t1, t1, t2, xx); eval_multiply(result, x); return; } else if(xx.compare(fp_type(1 - 1e-4f)) > 0) { T dx1; T t1, t2; eval_subtract(dx1, ui_type(1), xx); t1 = fp_type(0.5f); t2 = fp_type(1.5f); eval_ldexp(dx1, dx1, -1); hyp2F1(result, t1, t1, t2, dx1); eval_ldexp(dx1, dx1, 2); eval_sqrt(t1, dx1); eval_multiply(result, t1); eval_ldexp(t1, get_constant_pi<T>(), -1); result.negate(); eval_add(result, t1); if(b_neg) result.negate(); return; } #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS typedef typename boost::multiprecision::detail::canonical<long double, T>::type guess_type; #else typedef fp_type guess_type; #endif // Get initial estimate using standard math function asin. guess_type dd; eval_convert_to(&dd, xx); result = (guess_type)(std::asin(dd)); // Newton-Raphson iteration, we should double our precision with each iteration, // in practice this seems to not quite work in all cases... so terminate when we // have at least 2/3 of the digits correct on the assumption that the correction // we've just added will finish the job... boost::intmax_t current_precision = eval_ilogb(result); boost::intmax_t target_precision = current_precision - 1 - (std::numeric_limits<number<T> >::digits * 2) / 3; // Newton-Raphson iteration while(current_precision > target_precision) { T sine, cosine; eval_sin(sine, result); eval_cos(cosine, result); eval_subtract(sine, xx); eval_divide(sine, cosine); eval_subtract(result, sine); current_precision = eval_ilogb(sine); if(current_precision <= (std::numeric_limits<typename T::exponent_type>::min)() + 1) break; } if(b_neg) result.negate(); }
void eval_asin(T& result, const T& x) { BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The asin function is only valid for floating point types."); typedef typename boost::multiprecision::detail::canonical<boost::int32_t, T>::type si_type; typedef typename boost::multiprecision::detail::canonical<boost::uint32_t, T>::type ui_type; typedef typename T::exponent_type exp_type; typedef typename boost::multiprecision::detail::canonical<exp_type, T>::type canonical_exp_type; typedef typename mpl::front<typename T::float_types>::type fp_type; if(&result == &x) { T t(x); eval_asin(result, t); return; } switch(eval_fpclassify(x)) { case FP_NAN: case FP_INFINITE: result = std::numeric_limits<number<T, et_on> >::quiet_NaN().backend(); return; case FP_ZERO: result = ui_type(0); return; default: ; } const bool b_neg = eval_get_sign(x) < 0; T xx(x); if(b_neg) xx.negate(); int c = xx.compare(ui_type(1)); if(c > 0) { result = std::numeric_limits<number<T, et_on> >::quiet_NaN().backend(); return; } else if(c == 0) { result = get_constant_pi<T>(); eval_ldexp(result, result, -1); if(b_neg) result.negate(); return; } if(xx.compare(fp_type(1e-4)) < 0) { // http://functions.wolfram.com/ElementaryFunctions/ArcSin/26/01/01/ eval_multiply(xx, xx); T t1, t2; t1 = fp_type(0.5f); t2 = fp_type(1.5f); hyp2F1(result, t1, t1, t2, xx); eval_multiply(result, x); return; } else if(xx.compare(fp_type(1 - 1e-4f)) > 0) { T dx1; T t1, t2; eval_subtract(dx1, ui_type(1), xx); t1 = fp_type(0.5f); t2 = fp_type(1.5f); eval_ldexp(dx1, dx1, -1); hyp2F1(result, t1, t1, t2, dx1); eval_ldexp(dx1, dx1, 2); eval_sqrt(t1, dx1); eval_multiply(result, t1); eval_ldexp(t1, get_constant_pi<T>(), -1); result.negate(); eval_add(result, t1); if(b_neg) result.negate(); return; } // Get initial estimate using standard math function asin. double dd; eval_convert_to(&dd, xx); result = fp_type(std::asin(dd)); // Newton-Raphson iteration while(true) { T s, c; eval_sin(s, result); eval_cos(c, result); eval_subtract(s, xx); eval_divide(s, c); eval_subtract(result, s); T lim; eval_ldexp(lim, result, 1 - boost::multiprecision::detail::digits2<number<T, et_on> >::value); if(eval_get_sign(s) < 0) s.negate(); if(eval_get_sign(lim) < 0) lim.negate(); if(lim.compare(s) >= 0) break; } if(b_neg) result.negate(); }