gamma.hpp

来自「Boost provides free peer-reviewed portab」· HPP 代码 · 共 1,479 行 · 第 1/4 页

HPP
1,479
字号
   }   BOOST_MATH_INSTRUMENT_CODE(prefix);   if((floor(z) == z) && (z < max_factorial<T>::value))   {      prefix *= unchecked_factorial<T>(itrunc(z, pol) - 1);   }   else   {      prefix = prefix * pow(z / boost::math::constants::e<T>(), z);      BOOST_MATH_INSTRUMENT_CODE(prefix);      T sum = detail::lower_gamma_series(z, z, pol) / z;      BOOST_MATH_INSTRUMENT_CODE(sum);      sum += detail::upper_gamma_fraction(z, z, ::boost::math::policies::digits<T, Policy>());      BOOST_MATH_INSTRUMENT_CODE(sum);      if(fabs(tools::max_value<T>() / prefix) < fabs(sum))         return policies::raise_overflow_error<T>(function, "Result of tgamma is too large to represent.", pol);      BOOST_MATH_INSTRUMENT_CODE((sum * prefix));      return sum * prefix;   }   return prefix;}template <class T, class Policy>T lgamma_imp(T z, const Policy& pol, const lanczos::undefined_lanczos& l, int*sign){   BOOST_MATH_STD_USING   static const char* function = "boost::math::lgamma<%1%>(%1%)";   T result = 0;   int sresult = 1;   if(z <= 0)   {      if(floor(z) == z)         return policies::raise_pole_error<T>(function, "Evaluation of tgamma at a negative integer %1%.", z, pol);      T t = detail::sinpx(z);      z = -z;      if(t < 0)      {         t = -t;      }      else      {         sresult = -sresult;      }      result = log(boost::math::constants::pi<T>()) - lgamma_imp(z, pol, l, 0) - log(t);   }   else if((z != 1) && (z != 2))   {      T limit = (std::max)(z+1, T(10));      T prefix = z * log(limit) - limit;      T sum = detail::lower_gamma_series(z, limit, pol) / z;      sum += detail::upper_gamma_fraction(z, limit, ::boost::math::policies::digits<T, Policy>());      result = log(sum) + prefix;   }   if(sign)      *sign = sresult;   return result;}//// This helper calculates tgamma(dz+1)-1 without cancellation errors,// used by the upper incomplete gamma with z < 1://template <class T, class Policy, class L>T tgammap1m1_imp(T dz, Policy const& pol, const L& l){   BOOST_MATH_STD_USING   typedef typename policies::precision<T,Policy>::type precision_type;   typedef typename mpl::if_<      mpl::or_<         mpl::less_equal<precision_type, mpl::int_<0> >,         mpl::greater<precision_type, mpl::int_<113> >      >,      typename mpl::if_<         is_same<L, lanczos::lanczos24m113>,         mpl::int_<113>,         mpl::int_<0>      >::type,      typename mpl::if_<         mpl::less_equal<precision_type, mpl::int_<64> >,         mpl::int_<64>, mpl::int_<113> >::type       >::type tag_type;   T result;   if(dz < 0)   {      if(dz < -0.5)      {         // Best method is simply to subtract 1 from tgamma:         result = boost::math::tgamma(1+dz, pol) - 1;         BOOST_MATH_INSTRUMENT_CODE(result);      }      else      {         // Use expm1 on lgamma:         result = boost::math::expm1(-boost::math::log1p(dz, pol)             + lgamma_small_imp(dz+2, dz + 1, dz, tag_type(), pol, l));         BOOST_MATH_INSTRUMENT_CODE(result);      }   }   else   {      if(dz < 2)      {         // Use expm1 on lgamma:         result = boost::math::expm1(lgamma_small_imp(dz+1, dz, dz-1, tag_type(), pol, l), pol);         BOOST_MATH_INSTRUMENT_CODE(result);      }      else      {         // Best method is simply to subtract 1 from tgamma:         result = boost::math::tgamma(1+dz, pol) - 1;         BOOST_MATH_INSTRUMENT_CODE(result);      }   }   return result;}template <class T, class Policy>inline T tgammap1m1_imp(T dz, Policy const& pol,                 const ::boost::math::lanczos::undefined_lanczos& l){   BOOST_MATH_STD_USING // ADL of std names   //   // There should be a better solution than this, but the   // algebra isn't easy for the general case....   // Start by subracting 1 from tgamma:   //   T result = gamma_imp(1 + dz, pol, l) - 1;   BOOST_MATH_INSTRUMENT_CODE(result);   //   // Test the level of cancellation error observed: we loose one bit   // for each power of 2 the result is less than 1.  If we would get   // more bits from our most precise lgamma rational approximation,    // then use that instead:   //   BOOST_MATH_INSTRUMENT_CODE((dz > -0.5));   BOOST_MATH_INSTRUMENT_CODE((dz < 2));   BOOST_MATH_INSTRUMENT_CODE((ldexp(1.0, boost::math::policies::digits<T, Policy>()) * fabs(result) < 1e34));   if((dz > -0.5) && (dz < 2) && (ldexp(1.0, boost::math::policies::digits<T, Policy>()) * fabs(result) < 1e34))   {      result = tgammap1m1_imp(dz, pol, boost::math::lanczos::lanczos24m113());      BOOST_MATH_INSTRUMENT_CODE(result);   }   return result;}//// Series representation for upper fraction when z is small://template <class T>struct small_gamma2_series{   typedef T result_type;   small_gamma2_series(T a_, T x_) : result(-x_), x(-x_), apn(a_+1), n(1){}   T operator()()   {      T r = result / (apn);      result *= x;      result /= ++n;      apn += 1;      return r;   }private:   T result, x, apn;   int n;};//// calculate power term prefix (z^a)(e^-z) used in the non-normalised// incomplete gammas://template <class T, class Policy>T full_igamma_prefix(T a, T z, const Policy& pol){   BOOST_MATH_STD_USING   T prefix;   T alz = a * log(z);   if(z >= 1)   {      if((alz < tools::log_max_value<T>()) && (-z > tools::log_min_value<T>()))      {         prefix = pow(z, a) * exp(-z);      }      else if(a >= 1)      {         prefix = pow(z / exp(z/a), a);      }      else      {         prefix = exp(alz - z);      }   }   else   {      if(alz > tools::log_min_value<T>())      {         prefix = pow(z, a) * exp(-z);      }      else if(z/a < tools::log_max_value<T>())      {         prefix = pow(z / exp(z/a), a);      }      else      {         prefix = exp(alz - z);      }   }   //   // This error handling isn't very good: it happens after the fact   // rather than before it...   //   if((boost::math::fpclassify)(prefix) == (int)FP_INFINITE)      policies::raise_overflow_error<T>("boost::math::detail::full_igamma_prefix<%1%>(%1%, %1%)", "Result of incomplete gamma function is too large to represent.", pol);   return prefix;}//// Compute (z^a)(e^-z)/tgamma(a)// most if the error occurs in this function://template <class T, class Policy, class L>T regularised_gamma_prefix(T a, T z, const Policy& pol, const L& l){   BOOST_MATH_STD_USING   T agh = a + static_cast<T>(L::g()) - T(0.5);   T prefix;   T d = ((z - a) - static_cast<T>(L::g()) + T(0.5)) / agh;   if(a < 1)   {      //      // We have to treat a < 1 as a special case because our Lanczos      // approximations are optimised against the factorials with a > 1,      // and for high precision types especially (128-bit reals for example)      // very small values of a can give rather eroneous results for gamma      // unless we do this:      //      // TODO: is this still required?  Lanczos approx should be better now?      //      if(z <= tools::log_min_value<T>())      {         // Oh dear, have to use logs, should be free of cancellation errors though:         return exp(a * log(z) - z - lgamma_imp(a, pol, l));      }      else      {         // direct calculation, no danger of overflow as gamma(a) < 1/a         // for small a.         return pow(z, a) * exp(-z) / gamma_imp(a, pol, l);      }   }   else if((fabs(d*d*a) <= 100) && (a > 150))   {      // special case for large a and a ~ z.      prefix = a * boost::math::log1pmx(d, pol) + z * static_cast<T>(0.5 - L::g()) / agh;      prefix = exp(prefix);   }   else   {      //      // general case.      // direct computation is most accurate, but use various fallbacks      // for different parts of the problem domain:      //      T alz = a * log(z / agh);      T amz = a - z;      if(((std::min)(alz, amz) <= tools::log_min_value<T>()) || ((std::max)(alz, amz) >= tools::log_max_value<T>()))      {         T amza = amz / a;         if(((std::min)(alz, amz)/2 > tools::log_min_value<T>()) && ((std::max)(alz, amz)/2 < tools::log_max_value<T>()))         {            // compute square root of the result and then square it:            T sq = pow(z / agh, a / 2) * exp(amz / 2);            prefix = sq * sq;         }         else if(((std::min)(alz, amz)/4 > tools::log_min_value<T>()) && ((std::max)(alz, amz)/4 < tools::log_max_value<T>()) && (z > a))         {            // compute the 4th root of the result then square it twice:            T sq = pow(z / agh, a / 4) * exp(amz / 4);            prefix = sq * sq;            prefix *= prefix;         }         else if((amza > tools::log_min_value<T>()) && (amza < tools::log_max_value<T>()))         {            prefix = pow((z * exp(amza)) / agh, a);         }         else         {            prefix = exp(alz + amz);         }      }      else      {         prefix = pow(z / agh, a) * exp(amz);      }   }   prefix *= sqrt(agh / boost::math::constants::e<T>()) / L::lanczos_sum_expG_scaled(a);   return prefix;}//// And again, without Lanczos support://template <class T, class Policy>T regularised_gamma_prefix(T a, T z, const Policy& pol, const lanczos::undefined_lanczos&){   BOOST_MATH_STD_USING   T limit = (std::max)(T(10), a);   T sum = detail::lower_gamma_series(a, limit, pol) / a;   sum += detail::upper_gamma_fraction(a, limit, ::boost::math::policies::digits<T, Policy>());   if(a < 10)   {      // special case for small a:      T prefix = pow(z / 10, a);      prefix *= exp(10-z);      if(0 == prefix)      {         prefix = pow((z * exp((10-z)/a)) / 10, a);      }      prefix /= sum;      return prefix;   }   T zoa = z / a;   T amz = a - z;   T alzoa = a * log(zoa);   T prefix;   if(((std::min)(alzoa, amz) <= tools::log_min_value<T>()) || ((std::max)(alzoa, amz) >= tools::log_max_value<T>()))   {      T amza = amz / a;      if((amza <= tools::log_min_value<T>()) || (amza >= tools::log_max_value<T>()))      {         prefix = exp(alzoa + amz);      }      else      {         prefix = pow(zoa * exp(amza), a);      }   }   else   {      prefix = pow(zoa, a) * exp(amz);   }   prefix /= sum;   return prefix;}//// Upper gamma fraction for very small a://template <class T, class Policy>inline T tgamma_small_upper_part(T a, T x, const Policy& pol){   BOOST_MATH_STD_USING  // ADL of std functions.   //   // Compute the full upper fraction (Q) when a is very small:   //   T result;   result = boost::math::tgamma1pm1(a, pol) - boost::math::powm1(x, a, pol);   result /= a;   detail::small_gamma2_series<T> s(a, x);   boost::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();#if BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x582))

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?