add boost on mac

This commit is contained in:
Bassem Girgis
2019-08-10 16:38:17 -05:00
parent 861b918727
commit be945cb63b
14105 changed files with 2714968 additions and 0 deletions

View File

@@ -0,0 +1,333 @@
// Copyright 2011 John Maddock. Distributed under the Boost
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
//
// This file has no include guards or namespaces - it's expanded inline inside default_ops.hpp
//
template <class T>
void calc_log2(T& num, unsigned digits)
{
typedef typename boost::multiprecision::detail::canonical<boost::uint32_t, T>::type ui_type;
typedef typename mpl::front<typename T::signed_types>::type si_type;
//
// String value with 1100 digits:
//
static const char* string_val = "0."
"6931471805599453094172321214581765680755001343602552541206800094933936219696947156058633269964186875"
"4200148102057068573368552023575813055703267075163507596193072757082837143519030703862389167347112335"
"0115364497955239120475172681574932065155524734139525882950453007095326366642654104239157814952043740"
"4303855008019441706416715186447128399681717845469570262716310645461502572074024816377733896385506952"
"6066834113727387372292895649354702576265209885969320196505855476470330679365443254763274495125040606"
"9438147104689946506220167720424524529612687946546193165174681392672504103802546259656869144192871608"
"2938031727143677826548775664850856740776484514644399404614226031930967354025744460703080960850474866"
"3852313818167675143866747664789088143714198549423151997354880375165861275352916610007105355824987941"
"4729509293113897155998205654392871700072180857610252368892132449713893203784393530887748259701715591"
"0708823683627589842589185353024363421436706118923678919237231467232172053401649256872747782344535347"
"6481149418642386776774406069562657379600867076257199184734022651462837904883062033061144630073719489";
//
// Check if we can just construct from string:
//
if(digits < 3640) // 3640 binary digits ~ 1100 decimal digits
{
num = string_val;
return;
}
//
// We calculate log2 from using the formula:
//
// ln(2) = 3/4 SUM[n>=0] ((-1)^n * N!^2 / (2^n(2n+1)!))
//
// Numerator and denominator are calculated separately and then
// divided at the end, we also precalculate the terms up to n = 5
// since these fit in a 32-bit integer anyway.
//
// See Gourdon, X., and Sebah, P. The logarithmic constant: log 2, Jan. 2004.
// Also http://www.mpfr.org/algorithms.pdf.
//
num = static_cast<ui_type>(1180509120uL);
T denom, next_term, temp;
denom = static_cast<ui_type>(1277337600uL);
next_term = static_cast<ui_type>(120uL);
si_type sign = -1;
ui_type limit = digits / 3 + 1;
for(ui_type n = 6; n < limit; ++n)
{
temp = static_cast<ui_type>(2);
eval_multiply(temp, ui_type(2 * n));
eval_multiply(temp, ui_type(2 * n + 1));
eval_multiply(num, temp);
eval_multiply(denom, temp);
sign = -sign;
eval_multiply(next_term, n);
eval_multiply(temp, next_term, next_term);
if(sign < 0)
temp.negate();
eval_add(num, temp);
}
eval_multiply(denom, ui_type(4));
eval_multiply(num, ui_type(3));
INSTRUMENT_BACKEND(denom);
INSTRUMENT_BACKEND(num);
eval_divide(num, denom);
INSTRUMENT_BACKEND(num);
}
template <class T>
void calc_e(T& result, unsigned digits)
{
typedef typename mpl::front<typename T::unsigned_types>::type ui_type;
//
// 1100 digits in string form:
//
const char* string_val = "2."
"7182818284590452353602874713526624977572470936999595749669676277240766303535475945713821785251664274"
"2746639193200305992181741359662904357290033429526059563073813232862794349076323382988075319525101901"
"1573834187930702154089149934884167509244761460668082264800168477411853742345442437107539077744992069"
"5517027618386062613313845830007520449338265602976067371132007093287091274437470472306969772093101416"
"9283681902551510865746377211125238978442505695369677078544996996794686445490598793163688923009879312"
"7736178215424999229576351482208269895193668033182528869398496465105820939239829488793320362509443117"
"3012381970684161403970198376793206832823764648042953118023287825098194558153017567173613320698112509"
"9618188159304169035159888851934580727386673858942287922849989208680582574927961048419844436346324496"
"8487560233624827041978623209002160990235304369941849146314093431738143640546253152096183690888707016"
"7683964243781405927145635490613031072085103837505101157477041718986106873969655212671546889570350354"
"0212340784981933432106817012100562788023519303322474501585390473041995777709350366041699732972508869";
//
// Check if we can just construct from string:
//
if(digits < 3640) // 3640 binary digits ~ 1100 decimal digits
{
result = string_val;
return;
}
T lim;
lim = ui_type(1);
eval_ldexp(lim, lim, digits);
//
// Standard evaluation from the definition of e: http://functions.wolfram.com/Constants/E/02/
//
result = ui_type(2);
T denom;
denom = ui_type(1);
ui_type i = 2;
do{
eval_multiply(denom, i);
eval_multiply(result, i);
eval_add(result, ui_type(1));
++i;
}while(denom.compare(lim) <= 0);
eval_divide(result, denom);
}
template <class T>
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);
}
template <class T, const T& (*F)(void)>
struct constant_initializer
{
static void do_nothing()
{
init.do_nothing();
}
private:
struct initializer
{
initializer()
{
F();
}
void do_nothing()const{}
};
static const initializer init;
};
template <class T, const T& (*F)(void)>
typename constant_initializer<T, F>::initializer const constant_initializer<T, F>::init;
template <class T>
const T& get_constant_ln2()
{
static BOOST_MP_THREAD_LOCAL T result;
static BOOST_MP_THREAD_LOCAL long digits = 0;
#ifndef BOOST_MP_USING_THREAD_LOCAL
static BOOST_MP_THREAD_LOCAL bool b = false;
constant_initializer<T, &get_constant_ln2<T> >::do_nothing();
if(!b || (digits != boost::multiprecision::detail::digits2<number<T> >::value()))
{
b = true;
#else
if ((digits != boost::multiprecision::detail::digits2<number<T> >::value()))
{
#endif
calc_log2(result, boost::multiprecision::detail::digits2<number<T, et_on> >::value());
digits = boost::multiprecision::detail::digits2<number<T> >::value();
}
return result;
}
template <class T>
const T& get_constant_e()
{
static BOOST_MP_THREAD_LOCAL T result;
static BOOST_MP_THREAD_LOCAL long digits = 0;
#ifndef BOOST_MP_USING_THREAD_LOCAL
static BOOST_MP_THREAD_LOCAL bool b = false;
constant_initializer<T, &get_constant_e<T> >::do_nothing();
if (!b || (digits != boost::multiprecision::detail::digits2<number<T> >::value()))
{
b = true;
#else
if ((digits != boost::multiprecision::detail::digits2<number<T> >::value()))
{
#endif
calc_e(result, boost::multiprecision::detail::digits2<number<T, et_on> >::value());
digits = boost::multiprecision::detail::digits2<number<T> >::value();
}
return result;
}
template <class T>
const T& get_constant_pi()
{
static BOOST_MP_THREAD_LOCAL T result;
static BOOST_MP_THREAD_LOCAL long digits = 0;
#ifndef BOOST_MP_USING_THREAD_LOCAL
static BOOST_MP_THREAD_LOCAL bool b = false;
constant_initializer<T, &get_constant_pi<T> >::do_nothing();
if (!b || (digits != boost::multiprecision::detail::digits2<number<T> >::value()))
{
b = true;
#else
if ((digits != boost::multiprecision::detail::digits2<number<T> >::value()))
{
#endif
calc_pi(result, boost::multiprecision::detail::digits2<number<T, et_on> >::value());
digits = boost::multiprecision::detail::digits2<number<T> >::value();
}
return result;
}
template <class T>
const T& get_constant_one_over_epsilon()
{
static BOOST_MP_THREAD_LOCAL T result;
static BOOST_MP_THREAD_LOCAL long digits = 0;
#ifndef BOOST_MP_USING_THREAD_LOCAL
static BOOST_MP_THREAD_LOCAL bool b = false;
constant_initializer<T, &get_constant_one_over_epsilon<T> >::do_nothing();
if (!b || (digits != boost::multiprecision::detail::digits2<number<T> >::value()))
{
b = true;
#else
if ((digits != boost::multiprecision::detail::digits2<number<T> >::value()))
{
#endif
typedef typename mpl::front<typename T::unsigned_types>::type ui_type;
result = static_cast<ui_type>(1u);
eval_divide(result, std::numeric_limits<number<T> >::epsilon().backend());
}
return result;
}

View File

@@ -0,0 +1,910 @@
// Copyright Christopher Kormanyos 2002 - 2013.
// Copyright 2011 - 2013 John Maddock. Distributed under the Boost
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// This work is based on an earlier work:
// "Algorithm 910: A Portable C++ Multiple-Precision System for Special-Function Calculations",
// in ACM TOMS, {VOL 37, ISSUE 4, (February 2011)} (C) ACM, 2011. http://doi.acm.org/10.1145/1916461.1916469
//
// This file has no include guards or namespaces - it's expanded inline inside default_ops.hpp
//
#ifdef BOOST_MSVC
#pragma warning(push)
#pragma warning(disable:6326) // comparison of two constants
#endif
namespace detail{
template<typename T, typename U>
inline void pow_imp(T& result, const T& t, const U& p, const mpl::false_&)
{
// Compute the pure power of typename T t^p.
// Use the S-and-X binary method, as described in
// D. E. Knuth, "The Art of Computer Programming", Vol. 2,
// Section 4.6.3 . The resulting computational complexity
// is order log2[abs(p)].
typedef typename boost::multiprecision::detail::canonical<U, T>::type int_type;
if(&result == &t)
{
T temp;
pow_imp(temp, t, p, mpl::false_());
result = temp;
return;
}
// This will store the result.
if(U(p % U(2)) != U(0))
{
result = t;
}
else
result = int_type(1);
U p2(p);
// The variable x stores the binary powers of t.
T x(t);
while(U(p2 /= 2) != U(0))
{
// Square x for each binary power.
eval_multiply(x, x);
const bool has_binary_power = (U(p2 % U(2)) != U(0));
if(has_binary_power)
{
// Multiply the result with each binary power contained in the exponent.
eval_multiply(result, x);
}
}
}
template<typename T, typename U>
inline void pow_imp(T& result, const T& t, const U& p, const mpl::true_&)
{
// Signed integer power, just take care of the sign then call the unsigned version:
typedef typename boost::multiprecision::detail::canonical<U, T>::type int_type;
typedef typename make_unsigned<U>::type ui_type;
if(p < 0)
{
T temp;
temp = static_cast<int_type>(1);
T denom;
pow_imp(denom, t, static_cast<ui_type>(-p), mpl::false_());
eval_divide(result, temp, denom);
return;
}
pow_imp(result, t, static_cast<ui_type>(p), mpl::false_());
}
} // namespace detail
template<typename T, typename U>
inline typename enable_if_c<is_integral<U>::value>::type eval_pow(T& result, const T& t, const U& p)
{
detail::pow_imp(result, t, p, boost::is_signed<U>());
}
template <class T>
void hyp0F0(T& H0F0, const T& x)
{
// Compute the series representation of Hypergeometric0F0 taken from
// http://functions.wolfram.com/HypergeometricFunctions/Hypergeometric0F0/06/01/
// There are no checks on input range or parameter boundaries.
typedef typename mpl::front<typename T::unsigned_types>::type ui_type;
BOOST_ASSERT(&H0F0 != &x);
long tol = boost::multiprecision::detail::digits2<number<T, et_on> >::value();
T t;
T x_pow_n_div_n_fact(x);
eval_add(H0F0, x_pow_n_div_n_fact, ui_type(1));
T lim;
eval_ldexp(lim, H0F0, 1 - tol);
if(eval_get_sign(lim) < 0)
lim.negate();
ui_type n;
const unsigned series_limit =
boost::multiprecision::detail::digits2<number<T, et_on> >::value() < 100
? 100 : boost::multiprecision::detail::digits2<number<T, et_on> >::value();
// Series expansion of hyperg_0f0(; ; x).
for(n = 2; n < series_limit; ++n)
{
eval_multiply(x_pow_n_div_n_fact, x);
eval_divide(x_pow_n_div_n_fact, n);
eval_add(H0F0, x_pow_n_div_n_fact);
bool neg = eval_get_sign(x_pow_n_div_n_fact) < 0;
if(neg)
x_pow_n_div_n_fact.negate();
if(lim.compare(x_pow_n_div_n_fact) > 0)
break;
if(neg)
x_pow_n_div_n_fact.negate();
}
if(n >= series_limit)
BOOST_THROW_EXCEPTION(std::runtime_error("H0F0 failed to converge"));
}
template <class T>
void hyp1F0(T& H1F0, const T& a, const T& x)
{
// Compute the series representation of Hypergeometric1F0 taken from
// http://functions.wolfram.com/HypergeometricFunctions/Hypergeometric1F0/06/01/01/
// and also see the corresponding section for the power function (i.e. x^a).
// There are no checks on input range or parameter boundaries.
typedef typename boost::multiprecision::detail::canonical<int, T>::type si_type;
BOOST_ASSERT(&H1F0 != &x);
BOOST_ASSERT(&H1F0 != &a);
T x_pow_n_div_n_fact(x);
T pochham_a (a);
T ap (a);
eval_multiply(H1F0, pochham_a, x_pow_n_div_n_fact);
eval_add(H1F0, si_type(1));
T lim;
eval_ldexp(lim, H1F0, 1 - boost::multiprecision::detail::digits2<number<T, et_on> >::value());
if(eval_get_sign(lim) < 0)
lim.negate();
si_type n;
T term, part;
const si_type series_limit =
boost::multiprecision::detail::digits2<number<T, et_on> >::value() < 100
? 100 : boost::multiprecision::detail::digits2<number<T, et_on> >::value();
// Series expansion of hyperg_1f0(a; ; x).
for(n = 2; n < series_limit; n++)
{
eval_multiply(x_pow_n_div_n_fact, x);
eval_divide(x_pow_n_div_n_fact, n);
eval_increment(ap);
eval_multiply(pochham_a, ap);
eval_multiply(term, pochham_a, x_pow_n_div_n_fact);
eval_add(H1F0, term);
if(eval_get_sign(term) < 0)
term.negate();
if(lim.compare(term) >= 0)
break;
}
if(n >= series_limit)
BOOST_THROW_EXCEPTION(std::runtime_error("H1F0 failed to converge"));
}
template <class 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;
// Handle special arguments.
int type = eval_fpclassify(x);
bool isneg = eval_get_sign(x) < 0;
if(type == (int)FP_NAN)
{
result = x;
errno = EDOM;
return;
}
else if(type == (int)FP_INFINITE)
{
if(isneg)
result = ui_type(0u);
else
result = x;
return;
}
else if(type == (int)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.
if(xx.compare(si_type(1)) <= 0)
{
//
// Use series for exp(x) - 1:
//
T lim;
if(std::numeric_limits<number<T, et_on> >::is_specialized)
lim = std::numeric_limits<number<T, et_on> >::epsilon().backend();
else
{
result = ui_type(1);
eval_ldexp(lim, result, 1 - boost::multiprecision::detail::digits2<number<T, et_on> >::value());
}
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;
}
else if(exp_series.compare(x) == 0)
{
// We have a value that has no fractional part, but is too large to fit
// in a long long, in this situation the code below will fail, so
// we're just going to assume that this will overflow:
if(isneg)
result = ui_type(0);
else
result = std::numeric_limits<number<T> >::has_infinity ? std::numeric_limits<number<T> >::infinity().backend() : (std::numeric_limits<number<T> >::max)().backend();
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);
if (n == (std::numeric_limits<exp_type>::max)())
{
// Exponent is too large to fit in our exponent type:
if (isneg)
result = ui_type(0);
else
result = std::numeric_limits<number<T> >::has_infinity ? std::numeric_limits<number<T> >::infinity().backend() : (std::numeric_limits<number<T> >::max)().backend();
return;
}
// The scaling is 2^11 = 2048.
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;
}
template <class T>
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<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;
int s = eval_signbit(arg);
switch(eval_fpclassify(arg))
{
case FP_NAN:
result = arg;
errno = EDOM;
return;
case FP_INFINITE:
if(s) break;
result = arg;
return;
case FP_ZERO:
result = std::numeric_limits<number<T> >::has_infinity ? std::numeric_limits<number<T> >::infinity().backend() : (std::numeric_limits<number<T> >::max)().backend();
result.negate();
errno = ERANGE;
return;
}
if(s)
{
result = std::numeric_limits<number<T> >::quiet_NaN().backend();
errno = EDOM;
return;
}
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);
if(std::numeric_limits<number<T, et_on> >::is_specialized)
eval_multiply(lim, result, std::numeric_limits<number<T, et_on> >::epsilon().backend());
else
eval_ldexp(lim, result, 1 - boost::multiprecision::detail::digits2<number<T, et_on> >::value());
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);
}
template <class T>
const T& get_constant_log10()
{
static BOOST_MP_THREAD_LOCAL T result;
static BOOST_MP_THREAD_LOCAL long digits = 0;
#ifndef BOOST_MP_USING_THREAD_LOCAL
static BOOST_MP_THREAD_LOCAL bool b = false;
constant_initializer<T, &get_constant_log10<T> >::do_nothing();
if (!b || (digits != boost::multiprecision::detail::digits2<number<T> >::value()))
{
b = true;
#else
if ((digits != boost::multiprecision::detail::digits2<number<T> >::value()))
{
#endif
typedef typename boost::multiprecision::detail::canonical<unsigned, T>::type ui_type;
T ten;
ten = ui_type(10u);
eval_log(result, ten);
digits = boost::multiprecision::detail::digits2<number<T> >::value();
}
return result;
}
template <class T>
void eval_log10(T& result, const T& arg)
{
BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The log10 function is only valid for floating point types.");
eval_log(result, arg);
eval_divide(result, get_constant_log10<T>());
}
template <class R, class T>
inline void eval_log2(R& result, const T& a)
{
eval_log(result, a);
eval_divide(result, get_constant_ln2<R>());
}
template<typename T>
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) || (x.compare(si_type(1)) == 0))
{
result = x;
return;
}
if(a.compare(si_type(0)) == 0)
{
result = si_type(1);
return;
}
int type = eval_fpclassify(x);
switch(type)
{
case FP_ZERO:
switch(eval_fpclassify(a))
{
case FP_ZERO:
result = si_type(1);
break;
case FP_NAN:
result = a;
break;
case FP_NORMAL:
{
// Need to check for a an odd integer as a special case:
try
{
typename boost::multiprecision::detail::canonical<boost::intmax_t, T>::type i;
eval_convert_to(&i, a);
if(a.compare(i) == 0)
{
if(eval_signbit(a))
{
if(i & 1)
{
result = std::numeric_limits<number<T> >::infinity().backend();
if(eval_signbit(x))
result.negate();
errno = ERANGE;
}
else
{
result = std::numeric_limits<number<T> >::infinity().backend();
errno = ERANGE;
}
}
else if(i & 1)
{
result = x;
}
else
result = si_type(0);
return;
}
}
catch(const std::exception&)
{
// fallthrough..
}
BOOST_FALLTHROUGH;
}
default:
if(eval_signbit(a))
{
result = std::numeric_limits<number<T> >::infinity().backend();
errno = ERANGE;
}
else
result = x;
break;
}
return;
case FP_NAN:
result = x;
errno = ERANGE;
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;
typename boost::multiprecision::detail::canonical<boost::intmax_t, T>::type max_an =
std::numeric_limits<typename boost::multiprecision::detail::canonical<boost::intmax_t, T>::type>::is_specialized ?
(std::numeric_limits<typename boost::multiprecision::detail::canonical<boost::intmax_t, T>::type>::max)() :
static_cast<typename boost::multiprecision::detail::canonical<boost::intmax_t, T>::type>(1) << (sizeof(typename boost::multiprecision::detail::canonical<boost::intmax_t, T>::type) * CHAR_BIT - 2);
typename boost::multiprecision::detail::canonical<boost::intmax_t, T>::type min_an =
std::numeric_limits<typename boost::multiprecision::detail::canonical<boost::intmax_t, T>::type>::is_specialized ?
(std::numeric_limits<typename boost::multiprecision::detail::canonical<boost::intmax_t, T>::type>::min)() :
-min_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
eval_floor(result, a);
// -1^INF is a special case in C99:
if((x.compare(si_type(-1)) == 0) && (eval_fpclassify(a) == FP_INFINITE))
{
result = si_type(1);
}
else if(a.compare(result) == 0)
{
// exponent is so large we have no fractional part:
if(x.compare(si_type(-1)) < 0)
{
result = std::numeric_limits<number<T, et_on> >::infinity().backend();
}
else
{
result = si_type(0);
}
}
else if(type == FP_INFINITE)
{
result = std::numeric_limits<number<T, et_on> >::infinity().backend();
}
else 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 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) && (an < max_an) && (an > min_an))
{
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);
}
}
}
template<class T, class A>
#if BOOST_WORKAROUND(BOOST_MSVC, < 1800)
inline typename enable_if_c<!is_integral<A>::value, void>::type
#else
inline typename enable_if_c<is_compatible_arithmetic_type<A, number<T> >::value && !is_integral<A>::value, void>::type
#endif
eval_pow(T& result, const T& x, const A& a)
{
// Note this one is restricted to float arguments since pow.hpp already has a version for
// integer powers....
typedef typename boost::multiprecision::detail::canonical<A, T>::type canonical_type;
typedef typename mpl::if_<is_same<A, canonical_type>, T, canonical_type>::type cast_type;
cast_type c;
c = a;
eval_pow(result, x, c);
}
template<class T, class A>
#if BOOST_WORKAROUND(BOOST_MSVC, < 1800)
inline void
#else
inline typename enable_if_c<is_compatible_arithmetic_type<A, number<T> >::value, void>::type
#endif
eval_pow(T& result, const A& x, const T& a)
{
typedef typename boost::multiprecision::detail::canonical<A, T>::type canonical_type;
typedef typename mpl::if_<is_same<A, canonical_type>, T, canonical_type>::type cast_type;
cast_type c;
c = x;
eval_pow(result, c, a);
}
template <class T>
void eval_exp2(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.");
// Check for pure-integer arguments which can be either signed or unsigned.
typename boost::multiprecision::detail::canonical<typename T::exponent_type, T>::type i;
T temp;
try {
eval_trunc(temp, arg);
eval_convert_to(&i, temp);
if(arg.compare(i) == 0)
{
temp = static_cast<typename mpl::front<typename T::unsigned_types>::type>(1u);
eval_ldexp(result, temp, i);
return;
}
}
catch(const boost::math::rounding_error&)
{ /* Fallthrough */ }
catch(const std::runtime_error&)
{ /* Fallthrough */ }
temp = static_cast<typename mpl::front<typename T::unsigned_types>::type>(2u);
eval_pow(result, temp, arg);
}
namespace detail{
template <class T>
void small_sinh_series(T x, T& result)
{
typedef typename boost::multiprecision::detail::canonical<unsigned, T>::type ui_type;
bool neg = eval_get_sign(x) < 0;
if(neg)
x.negate();
T p(x);
T mult(x);
eval_multiply(mult, x);
result = x;
ui_type k = 1;
T lim(x);
eval_ldexp(lim, lim, 1 - boost::multiprecision::detail::digits2<number<T, et_on> >::value());
do
{
eval_multiply(p, mult);
eval_divide(p, ++k);
eval_divide(p, ++k);
eval_add(result, p);
}while(p.compare(lim) >= 0);
if(neg)
result.negate();
}
template <class T>
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:
errno = EDOM;
// fallthrough...
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(eval_signbit(e_mx) != eval_signbit(e_px))
e_mx.negate(); // Handles lack of signed zero in some types
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);
}
}
} // namespace detail
template <class T>
inline void eval_sinh(T& result, const T& x)
{
BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The sinh function is only valid for floating point types.");
detail::sinhcosh(x, &result, static_cast<T*>(0));
}
template <class T>
inline void eval_cosh(T& result, const T& x)
{
BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The cosh function is only valid for floating point types.");
detail::sinhcosh(x, static_cast<T*>(0), &result);
}
template <class T>
inline void eval_tanh(T& result, const T& x)
{
BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The tanh function is only valid for floating point types.");
T c;
detail::sinhcosh(x, &result, &c);
if((eval_fpclassify(result) == FP_INFINITE) && (eval_fpclassify(c) == FP_INFINITE))
{
bool s = eval_signbit(result) != eval_signbit(c);
result = static_cast<typename mpl::front<typename T::unsigned_types>::type>(1u);
if(s)
result.negate();
return;
}
eval_divide(result, c);
}
#ifdef BOOST_MSVC
#pragma warning(pop)
#endif

View File

@@ -0,0 +1,844 @@
// Copyright Christopher Kormanyos 2002 - 2011.
// Copyright 2011 John Maddock. Distributed under the Boost
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
// This work is based on an earlier work:
// "Algorithm 910: A Portable C++ Multiple-Precision System for Special-Function Calculations",
// in ACM TOMS, {VOL 37, ISSUE 4, (February 2011)} (C) ACM, 2011. http://doi.acm.org/10.1145/1916461.1916469
//
// This file has no include guards or namespaces - it's expanded inline inside default_ops.hpp
//
#ifdef BOOST_MSVC
#pragma warning(push)
#pragma warning(disable:6326) // comparison of two constants
#endif
template <class T>
void hyp0F1(T& result, const T& b, const T& x)
{
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;
// Compute the series representation of Hypergeometric0F1 taken from
// http://functions.wolfram.com/HypergeometricFunctions/Hypergeometric0F1/06/01/01/
// There are no checks on input range or parameter boundaries.
T x_pow_n_div_n_fact(x);
T pochham_b (b);
T bp (b);
eval_divide(result, x_pow_n_div_n_fact, pochham_b);
eval_add(result, ui_type(1));
si_type n;
T tol;
tol = ui_type(1);
eval_ldexp(tol, tol, 1 - boost::multiprecision::detail::digits2<number<T, et_on> >::value());
eval_multiply(tol, result);
if(eval_get_sign(tol) < 0)
tol.negate();
T term;
const int series_limit =
boost::multiprecision::detail::digits2<number<T, et_on> >::value() < 100
? 100 : boost::multiprecision::detail::digits2<number<T, et_on> >::value();
// Series expansion of hyperg_0f1(; b; x).
for(n = 2; n < series_limit; ++n)
{
eval_multiply(x_pow_n_div_n_fact, x);
eval_divide(x_pow_n_div_n_fact, n);
eval_increment(bp);
eval_multiply(pochham_b, bp);
eval_divide(term, x_pow_n_div_n_fact, pochham_b);
eval_add(result, term);
bool neg_term = eval_get_sign(term) < 0;
if(neg_term)
term.negate();
if(term.compare(tol) <= 0)
break;
}
if(n >= series_limit)
BOOST_THROW_EXCEPTION(std::runtime_error("H0F1 Failed to Converge"));
}
template <class 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();
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: ;
}
// 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>());
if (n_pi.compare(get_constant_one_over_epsilon<T>()) > 0)
{
result = ui_type(0);
return;
}
else
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();
}
template <class T>
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();
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 = 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));
//
// If t is so large that all digits cancel the result of this subtraction
// is completely meaningless, just assume the result is zero for now...
//
// TODO We should of course do much better, see:
// "ARGUMENT REDUCTION FOR HUGE ARGUMENTS" K C Ng 1992
//
if (n_pi.compare(get_constant_one_over_epsilon<T>()) > 0)
{
result = ui_type(1);
return;
}
else
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();
}
template <class T>
void eval_tan(T& result, const T& x)
{
BOOST_STATIC_ASSERT_MSG(number_category<T>::value == number_kind_floating_point, "The tan function is only valid for floating point types.");
if(&result == &x)
{
T temp;
eval_tan(temp, x);
result = temp;
return;
}
T t;
eval_sin(result, x);
eval_cos(t, x);
eval_divide(result, t);
}
template <class T>
void hyp2F1(T& result, const T& a, const T& b, const T& c, const T& x)
{
// Compute the series representation of hyperg_2f1 taken from
// Abramowitz and Stegun 15.1.1.
// There are no checks on input range or parameter boundaries.
typedef typename boost::multiprecision::detail::canonical<boost::uint32_t, T>::type ui_type;
T x_pow_n_div_n_fact(x);
T pochham_a (a);
T pochham_b (b);
T pochham_c (c);
T ap (a);
T bp (b);
T cp (c);
eval_multiply(result, pochham_a, pochham_b);
eval_divide(result, pochham_c);
eval_multiply(result, x_pow_n_div_n_fact);
eval_add(result, ui_type(1));
T lim;
eval_ldexp(lim, result, 1 - boost::multiprecision::detail::digits2<number<T, et_on> >::value());
if(eval_get_sign(lim) < 0)
lim.negate();
ui_type n;
T term;
const unsigned series_limit =
boost::multiprecision::detail::digits2<number<T, et_on> >::value() < 100
? 100 : boost::multiprecision::detail::digits2<number<T, et_on> >::value();
// Series expansion of hyperg_2f1(a, b; c; x).
for(n = 2; n < series_limit; ++n)
{
eval_multiply(x_pow_n_div_n_fact, x);
eval_divide(x_pow_n_div_n_fact, n);
eval_increment(ap);
eval_multiply(pochham_a, ap);
eval_increment(bp);
eval_multiply(pochham_b, bp);
eval_increment(cp);
eval_multiply(pochham_c, cp);
eval_multiply(term, pochham_a, pochham_b);
eval_divide(term, pochham_c);
eval_multiply(term, x_pow_n_div_n_fact);
eval_add(result, term);
if(eval_get_sign(term) < 0)
term.negate();
if(lim.compare(term) >= 0)
break;
}
if(n > series_limit)
BOOST_THROW_EXCEPTION(std::runtime_error("H2F1 failed to converge."));
}
template <class T>
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();
}
template <class T>
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();
}
template <class 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();
}
template <class T>
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>());
}
}
template<class T, class A>
inline typename enable_if<is_arithmetic<A>, void>::type eval_atan2(T& result, const T& x, const A& a)
{
typedef typename boost::multiprecision::detail::canonical<A, T>::type canonical_type;
typedef typename mpl::if_<is_same<A, canonical_type>, T, canonical_type>::type cast_type;
cast_type c;
c = a;
eval_atan2(result, x, c);
}
template<class T, class A>
inline typename enable_if<is_arithmetic<A>, void>::type eval_atan2(T& result, const A& x, const T& a)
{
typedef typename boost::multiprecision::detail::canonical<A, T>::type canonical_type;
typedef typename mpl::if_<is_same<A, canonical_type>, T, canonical_type>::type cast_type;
cast_type c;
c = x;
eval_atan2(result, c, a);
}
#ifdef BOOST_MSVC
#pragma warning(pop)
#endif