log1p.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. // (C) Copyright John Maddock 2005-2006.
  2. // Use, modification and distribution are subject to the
  3. // Boost Software License, Version 1.0. (See accompanying file
  4. // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  5. #ifndef BOOST_MATH_LOG1P_INCLUDED
  6. #define BOOST_MATH_LOG1P_INCLUDED
  7. #ifdef _MSC_VER
  8. #pragma once
  9. #pragma warning(push)
  10. #pragma warning(disable:4702) // Unreachable code (release mode only warning)
  11. #endif
  12. #if defined __has_include
  13. # if ((__cplusplus > 202002L) || (defined(_MSVC_LANG) && (_MSVC_LANG > 202002L)))
  14. # if __has_include (<stdfloat>)
  15. # include <stdfloat>
  16. # endif
  17. # endif
  18. #endif
  19. #include <boost/math/tools/config.hpp>
  20. #include <boost/math/tools/series.hpp>
  21. #include <boost/math/tools/rational.hpp>
  22. #include <boost/math/tools/big_constant.hpp>
  23. #include <boost/math/tools/numeric_limits.hpp>
  24. #include <boost/math/tools/cstdint.hpp>
  25. #include <boost/math/tools/promotion.hpp>
  26. #include <boost/math/tools/precision.hpp>
  27. #include <boost/math/policies/error_handling.hpp>
  28. #include <boost/math/special_functions/math_fwd.hpp>
  29. #include <boost/math/tools/assert.hpp>
  30. #include <boost/math/special_functions/fpclassify.hpp>
  31. #if defined(__GNUC__) && defined(BOOST_MATH_USE_FLOAT128)
  32. //
  33. // This is the only way we can avoid
  34. // warning: non-standard suffix on floating constant [-Wpedantic]
  35. // when building with -Wall -pedantic. Neither __extension__
  36. // nor #pragma diagnostic ignored work :(
  37. //
  38. #pragma GCC system_header
  39. #endif
  40. namespace boost{ namespace math{
  41. namespace detail
  42. {
  43. // Functor log1p_series returns the next term in the Taylor series
  44. // pow(-1, k-1)*pow(x, k) / k
  45. // each time that operator() is invoked.
  46. //
  47. template <class T>
  48. struct log1p_series
  49. {
  50. typedef T result_type;
  51. BOOST_MATH_GPU_ENABLED log1p_series(T x)
  52. : k(0), m_mult(-x), m_prod(-1){}
  53. BOOST_MATH_GPU_ENABLED T operator()()
  54. {
  55. m_prod *= m_mult;
  56. return m_prod / ++k;
  57. }
  58. BOOST_MATH_GPU_ENABLED int count()const
  59. {
  60. return k;
  61. }
  62. private:
  63. int k;
  64. const T m_mult;
  65. T m_prod;
  66. log1p_series(const log1p_series&) = delete;
  67. log1p_series& operator=(const log1p_series&) = delete;
  68. };
  69. // Algorithm log1p is part of C99, but is not yet provided by many compilers.
  70. //
  71. // This version uses a Taylor series expansion for 0.5 > x > epsilon, which may
  72. // require up to std::numeric_limits<T>::digits+1 terms to be calculated.
  73. // It would be much more efficient to use the equivalence:
  74. // log(1+x) == (log(1+x) * x) / ((1-x) - 1)
  75. // Unfortunately many optimizing compilers make such a mess of this, that
  76. // it performs no better than log(1+x): which is to say not very well at all.
  77. //
  78. template <class T, class Policy>
  79. BOOST_MATH_GPU_ENABLED T log1p_imp(T const & x, const Policy& pol, const boost::math::integral_constant<int, 0>&)
  80. { // The function returns the natural logarithm of 1 + x.
  81. typedef typename tools::promote_args<T>::type result_type;
  82. BOOST_MATH_STD_USING
  83. constexpr auto function = "boost::math::log1p<%1%>(%1%)";
  84. if((x < -1) || (boost::math::isnan)(x))
  85. return policies::raise_domain_error<T>(function, "log1p(x) requires x > -1, but got x = %1%.", x, pol);
  86. if(x == -1)
  87. return -policies::raise_overflow_error<T>(function, nullptr, pol);
  88. result_type a = abs(result_type(x));
  89. if(a > result_type(0.5f))
  90. return log(1 + result_type(x));
  91. // Note that without numeric_limits specialisation support,
  92. // epsilon just returns zero, and our "optimisation" will always fail:
  93. if(a < tools::epsilon<result_type>())
  94. return x;
  95. detail::log1p_series<result_type> s(x);
  96. boost::math::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();
  97. result_type result = tools::sum_series(s, policies::get_epsilon<result_type, Policy>(), max_iter);
  98. policies::check_series_iterations<T>(function, max_iter, pol);
  99. return result;
  100. }
  101. template <class T, class Policy>
  102. BOOST_MATH_GPU_ENABLED T log1p_imp(T const& x, const Policy& pol, const boost::math::integral_constant<int, 53>&)
  103. { // The function returns the natural logarithm of 1 + x.
  104. BOOST_MATH_STD_USING
  105. constexpr auto function = "boost::math::log1p<%1%>(%1%)";
  106. if(x < -1)
  107. return policies::raise_domain_error<T>(function, "log1p(x) requires x > -1, but got x = %1%.", x, pol);
  108. if(x == -1)
  109. return -policies::raise_overflow_error<T>(function, nullptr, pol);
  110. T a = fabs(x);
  111. if(a > 0.5f)
  112. return log(1 + x);
  113. // Note that without numeric_limits specialisation support,
  114. // epsilon just returns zero, and our "optimisation" will always fail:
  115. if(a < tools::epsilon<T>())
  116. return x;
  117. // Maximum Deviation Found: 1.846e-017
  118. // Expected Error Term: 1.843e-017
  119. // Maximum Relative Change in Control Points: 8.138e-004
  120. // Max Error found at double precision = 3.250766e-016
  121. BOOST_MATH_STATIC const T P[] = {
  122. static_cast<T>(0.15141069795941984e-16L),
  123. static_cast<T>(0.35495104378055055e-15L),
  124. static_cast<T>(0.33333333333332835L),
  125. static_cast<T>(0.99249063543365859L),
  126. static_cast<T>(1.1143969784156509L),
  127. static_cast<T>(0.58052937949269651L),
  128. static_cast<T>(0.13703234928513215L),
  129. static_cast<T>(0.011294864812099712L)
  130. };
  131. BOOST_MATH_STATIC const T Q[] = {
  132. static_cast<T>(1L),
  133. static_cast<T>(3.7274719063011499L),
  134. static_cast<T>(5.5387948649720334L),
  135. static_cast<T>(4.159201143419005L),
  136. static_cast<T>(1.6423855110312755L),
  137. static_cast<T>(0.31706251443180914L),
  138. static_cast<T>(0.022665554431410243L),
  139. static_cast<T>(-0.29252538135177773e-5L)
  140. };
  141. T result = 1 - x / 2 + tools::evaluate_polynomial(P, x) / tools::evaluate_polynomial(Q, x);
  142. result *= x;
  143. return result;
  144. }
  145. template <class T, class Policy>
  146. BOOST_MATH_GPU_ENABLED T log1p_imp(T const& x, const Policy& pol, const boost::math::integral_constant<int, 64>&)
  147. { // The function returns the natural logarithm of 1 + x.
  148. BOOST_MATH_STD_USING
  149. constexpr auto function = "boost::math::log1p<%1%>(%1%)";
  150. if(x < -1)
  151. return policies::raise_domain_error<T>(function, "log1p(x) requires x > -1, but got x = %1%.", x, pol);
  152. if(x == -1)
  153. return -policies::raise_overflow_error<T>(function, nullptr, pol);
  154. T a = fabs(x);
  155. if(a > 0.5f)
  156. return log(1 + x);
  157. // Note that without numeric_limits specialisation support,
  158. // epsilon just returns zero, and our "optimisation" will always fail:
  159. if(a < tools::epsilon<T>())
  160. return x;
  161. // Maximum Deviation Found: 8.089e-20
  162. // Expected Error Term: 8.088e-20
  163. // Maximum Relative Change in Control Points: 9.648e-05
  164. // Max Error found at long double precision = 2.242324e-19
  165. BOOST_MATH_STATIC const T P[] = {
  166. BOOST_MATH_BIG_CONSTANT(T, 64, -0.807533446680736736712e-19),
  167. BOOST_MATH_BIG_CONSTANT(T, 64, -0.490881544804798926426e-18),
  168. BOOST_MATH_BIG_CONSTANT(T, 64, 0.333333333333333373941),
  169. BOOST_MATH_BIG_CONSTANT(T, 64, 1.17141290782087994162),
  170. BOOST_MATH_BIG_CONSTANT(T, 64, 1.62790522814926264694),
  171. BOOST_MATH_BIG_CONSTANT(T, 64, 1.13156411870766876113),
  172. BOOST_MATH_BIG_CONSTANT(T, 64, 0.408087379932853785336),
  173. BOOST_MATH_BIG_CONSTANT(T, 64, 0.0706537026422828914622),
  174. BOOST_MATH_BIG_CONSTANT(T, 64, 0.00441709903782239229447)
  175. };
  176. BOOST_MATH_STATIC const T Q[] = {
  177. BOOST_MATH_BIG_CONSTANT(T, 64, 1.0),
  178. BOOST_MATH_BIG_CONSTANT(T, 64, 4.26423872346263928361),
  179. BOOST_MATH_BIG_CONSTANT(T, 64, 7.48189472704477708962),
  180. BOOST_MATH_BIG_CONSTANT(T, 64, 6.94757016732904280913),
  181. BOOST_MATH_BIG_CONSTANT(T, 64, 3.6493508622280767304),
  182. BOOST_MATH_BIG_CONSTANT(T, 64, 1.06884863623790638317),
  183. BOOST_MATH_BIG_CONSTANT(T, 64, 0.158292216998514145947),
  184. BOOST_MATH_BIG_CONSTANT(T, 64, 0.00885295524069924328658),
  185. BOOST_MATH_BIG_CONSTANT(T, 64, -0.560026216133415663808e-6)
  186. };
  187. T result = 1 - x / 2 + tools::evaluate_polynomial(P, x) / tools::evaluate_polynomial(Q, x);
  188. result *= x;
  189. return result;
  190. }
  191. template <class T, class Policy>
  192. BOOST_MATH_GPU_ENABLED T log1p_imp(T const& x, const Policy& pol, const boost::math::integral_constant<int, 24>&)
  193. { // The function returns the natural logarithm of 1 + x.
  194. BOOST_MATH_STD_USING
  195. constexpr auto function = "boost::math::log1p<%1%>(%1%)";
  196. if(x < -1)
  197. return policies::raise_domain_error<T>(
  198. function, "log1p(x) requires x > -1, but got x = %1%.", x, pol);
  199. if(x == -1)
  200. return -policies::raise_overflow_error<T>(
  201. function, nullptr, pol);
  202. T a = fabs(x);
  203. if(a > 0.5f)
  204. return log(1 + x);
  205. // Note that without numeric_limits specialisation support,
  206. // epsilon just returns zero, and our "optimisation" will always fail:
  207. if(a < tools::epsilon<T>())
  208. return x;
  209. // Maximum Deviation Found: 6.910e-08
  210. // Expected Error Term: 6.910e-08
  211. // Maximum Relative Change in Control Points: 2.509e-04
  212. // Max Error found at double precision = 6.910422e-08
  213. // Max Error found at float precision = 8.357242e-08
  214. BOOST_MATH_STATIC const T P[] = {
  215. -0.671192866803148236519e-7L,
  216. 0.119670999140731844725e-6L,
  217. 0.333339469182083148598L,
  218. 0.237827183019664122066L
  219. };
  220. BOOST_MATH_STATIC const T Q[] = {
  221. 1L,
  222. 1.46348272586988539733L,
  223. 0.497859871350117338894L,
  224. -0.00471666268910169651936L
  225. };
  226. T result = 1 - x / 2 + tools::evaluate_polynomial(P, x) / tools::evaluate_polynomial(Q, x);
  227. result *= x;
  228. return result;
  229. }
  230. } // namespace detail
  231. template <class T, class Policy>
  232. BOOST_MATH_GPU_ENABLED inline typename tools::promote_args<T>::type log1p(T x, const Policy&)
  233. {
  234. typedef typename tools::promote_args<T>::type result_type;
  235. typedef typename policies::evaluation<result_type, Policy>::type value_type;
  236. typedef typename policies::precision<result_type, Policy>::type precision_type;
  237. typedef typename policies::normalise<
  238. Policy,
  239. policies::promote_float<false>,
  240. policies::promote_double<false>,
  241. policies::discrete_quantile<>,
  242. policies::assert_undefined<> >::type forwarding_policy;
  243. typedef boost::math::integral_constant<int,
  244. precision_type::value <= 0 ? 0 :
  245. precision_type::value <= 53 ? 53 :
  246. precision_type::value <= 64 ? 64 : 0
  247. > tag_type;
  248. return policies::checked_narrowing_cast<result_type, forwarding_policy>(
  249. detail::log1p_imp(static_cast<value_type>(x), forwarding_policy(), tag_type()), "boost::math::log1p<%1%>(%1%)");
  250. }
  251. template <class Policy>
  252. BOOST_MATH_GPU_ENABLED inline float log1p(float x, const Policy& pol)
  253. {
  254. if(x < -1)
  255. return policies::raise_domain_error<float>("log1p<%1%>(%1%)", "log1p(x) requires x > -1, but got x = %1%.", x, pol);
  256. if(x == -1)
  257. return -policies::raise_overflow_error<float>("log1p<%1%>(%1%)", nullptr, pol);
  258. #ifndef BOOST_MATH_HAS_NVRTC
  259. return std::log1p(x);
  260. #else
  261. return ::log1pf(x);
  262. #endif
  263. }
  264. #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
  265. template <class Policy>
  266. BOOST_MATH_GPU_ENABLED inline long double log1p(long double x, const Policy& pol)
  267. {
  268. if(x < -1)
  269. return policies::raise_domain_error<long double>("log1p<%1%>(%1%)", "log1p(x) requires x > -1, but got x = %1%.", x, pol);
  270. if(x == -1)
  271. return -policies::raise_overflow_error<long double>("log1p<%1%>(%1%)", nullptr, pol);
  272. return std::log1p(x);
  273. }
  274. #endif
  275. template <class Policy>
  276. BOOST_MATH_GPU_ENABLED inline double log1p(double x, const Policy& pol)
  277. {
  278. if(x < -1)
  279. return policies::raise_domain_error<double>("log1p<%1%>(%1%)", "log1p(x) requires x > -1, but got x = %1%.", x, pol);
  280. if(x == -1)
  281. return -policies::raise_overflow_error<double>("log1p<%1%>(%1%)", nullptr, pol);
  282. #ifndef BOOST_MATH_HAS_NVRTC
  283. return std::log1p(x);
  284. #else
  285. return ::log1p(x);
  286. #endif
  287. }
  288. template <class T>
  289. BOOST_MATH_GPU_ENABLED inline typename tools::promote_args<T>::type log1p(T x)
  290. {
  291. return boost::math::log1p(x, policies::policy<>());
  292. }
  293. //
  294. // Compute log(1+x)-x:
  295. //
  296. template <class T, class Policy>
  297. BOOST_MATH_GPU_ENABLED inline typename tools::promote_args<T>::type
  298. log1pmx(T x, const Policy& pol)
  299. {
  300. typedef typename tools::promote_args<T>::type result_type;
  301. BOOST_MATH_STD_USING
  302. constexpr auto function = "boost::math::log1pmx<%1%>(%1%)";
  303. if(x < -1)
  304. return policies::raise_domain_error<T>(function, "log1pmx(x) requires x > -1, but got x = %1%.", x, pol);
  305. if(x == -1)
  306. return -policies::raise_overflow_error<T>(function, nullptr, pol);
  307. result_type a = abs(result_type(x));
  308. if(a > result_type(0.95f))
  309. return log(1 + result_type(x)) - result_type(x);
  310. // Note that without numeric_limits specialisation support,
  311. // epsilon just returns zero, and our "optimisation" will always fail:
  312. if(a < tools::epsilon<result_type>())
  313. return -x * x / 2;
  314. boost::math::detail::log1p_series<T> s(x);
  315. s();
  316. boost::math::uintmax_t max_iter = policies::get_max_series_iterations<Policy>();
  317. T result = boost::math::tools::sum_series(s, policies::get_epsilon<T, Policy>(), max_iter);
  318. policies::check_series_iterations<T>(function, max_iter, pol);
  319. return result;
  320. }
  321. template <class T>
  322. BOOST_MATH_GPU_ENABLED inline typename tools::promote_args<T>::type log1pmx(T x)
  323. {
  324. return log1pmx(x, policies::policy<>());
  325. }
  326. //
  327. // Specific width floating point types:
  328. //
  329. #ifdef __STDCPP_FLOAT32_T__
  330. template <class Policy>
  331. BOOST_MATH_GPU_ENABLED inline std::float32_t log1p(std::float32_t x, const Policy& pol)
  332. {
  333. return boost::math::log1p(static_cast<float>(x), pol);
  334. }
  335. #endif
  336. #ifdef __STDCPP_FLOAT64_T__
  337. template <class Policy>
  338. BOOST_MATH_GPU_ENABLED inline std::float64_t log1p(std::float64_t x, const Policy& pol)
  339. {
  340. return boost::math::log1p(static_cast<double>(x), pol);
  341. }
  342. #endif
  343. #ifdef __STDCPP_FLOAT128_T__
  344. template <class Policy>
  345. BOOST_MATH_GPU_ENABLED inline std::float128_t log1p(std::float128_t x, const Policy& pol)
  346. {
  347. if constexpr (std::numeric_limits<long double>::digits == std::numeric_limits<std::float128_t>::digits)
  348. {
  349. return boost::math::log1p(static_cast<long double>(x), pol);
  350. }
  351. else
  352. {
  353. return boost::math::detail::log1p_imp(x, pol, boost::math::integral_constant<int, 0>());
  354. }
  355. }
  356. #endif
  357. } // namespace math
  358. } // namespace boost
  359. #ifdef _MSC_VER
  360. #pragma warning(pop)
  361. #endif
  362. #endif // BOOST_MATH_LOG1P_INCLUDED