next.hpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490
  1. // (C) Copyright John Maddock 2008.
  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_SPECIAL_NEXT_HPP
  6. #define BOOST_MATH_SPECIAL_NEXT_HPP
  7. #ifdef _MSC_VER
  8. #pragma once
  9. #endif
  10. #include <boost/math/policies/error_handling.hpp>
  11. #include <boost/math/special_functions/fpclassify.hpp>
  12. #include <boost/math/special_functions/sign.hpp>
  13. #include <boost/math/special_functions/trunc.hpp>
  14. #ifdef BOOST_MSVC
  15. #include <float.h>
  16. #endif
  17. namespace boost{ namespace math{
  18. namespace detail{
  19. template <class T>
  20. inline T get_smallest_value(mpl::true_ const&)
  21. {
  22. //
  23. // numeric_limits lies about denorms being present - particularly
  24. // when this can be turned on or off at runtime, as is the case
  25. // when using the SSE2 registers in DAZ or FTZ mode.
  26. //
  27. static const T m = std::numeric_limits<T>::denorm_min();
  28. return ((tools::min_value<T>() - m) == tools::min_value<T>()) ? tools::min_value<T>() : m;
  29. }
  30. template <class T>
  31. inline T get_smallest_value(mpl::false_ const&)
  32. {
  33. return tools::min_value<T>();
  34. }
  35. template <class T>
  36. inline T get_smallest_value()
  37. {
  38. #if defined(BOOST_MSVC) && (BOOST_MSVC <= 1310)
  39. return get_smallest_value<T>(mpl::bool_<std::numeric_limits<T>::is_specialized && (std::numeric_limits<T>::has_denorm == 1)>());
  40. #else
  41. return get_smallest_value<T>(mpl::bool_<std::numeric_limits<T>::is_specialized && (std::numeric_limits<T>::has_denorm == std::denorm_present)>());
  42. #endif
  43. }
  44. //
  45. // Returns the smallest value that won't generate denorms when
  46. // we calculate the value of the least-significant-bit:
  47. //
  48. template <class T>
  49. T get_min_shift_value();
  50. template <class T>
  51. struct min_shift_initializer
  52. {
  53. struct init
  54. {
  55. init()
  56. {
  57. do_init();
  58. }
  59. static void do_init()
  60. {
  61. get_min_shift_value<T>();
  62. }
  63. void force_instantiate()const{}
  64. };
  65. static const init initializer;
  66. static void force_instantiate()
  67. {
  68. initializer.force_instantiate();
  69. }
  70. };
  71. template <class T>
  72. const typename min_shift_initializer<T>::init min_shift_initializer<T>::initializer;
  73. template <class T>
  74. inline T get_min_shift_value()
  75. {
  76. BOOST_MATH_STD_USING
  77. static const T val = ldexp(tools::min_value<T>(), tools::digits<T>() + 1);
  78. min_shift_initializer<T>::force_instantiate();
  79. return val;
  80. }
  81. template <class T, class Policy>
  82. T float_next_imp(const T& val, const Policy& pol)
  83. {
  84. BOOST_MATH_STD_USING
  85. int expon;
  86. static const char* function = "float_next<%1%>(%1%)";
  87. int fpclass = (boost::math::fpclassify)(val);
  88. if((fpclass == FP_NAN) || (fpclass == FP_INFINITE))
  89. {
  90. if(val < 0)
  91. return -tools::max_value<T>();
  92. return policies::raise_domain_error<T>(
  93. function,
  94. "Argument must be finite, but got %1%", val, pol);
  95. }
  96. if(val >= tools::max_value<T>())
  97. return policies::raise_overflow_error<T>(function, 0, pol);
  98. if(val == 0)
  99. return detail::get_smallest_value<T>();
  100. if((fpclass != FP_SUBNORMAL) && (fpclass != FP_ZERO) && (fabs(val) < detail::get_min_shift_value<T>()) && (val != -tools::min_value<T>()))
  101. {
  102. //
  103. // Special case: if the value of the least significant bit is a denorm, and the result
  104. // would not be a denorm, then shift the input, increment, and shift back.
  105. // This avoids issues with the Intel SSE2 registers when the FTZ or DAZ flags are set.
  106. //
  107. return ldexp(float_next(T(ldexp(val, 2 * tools::digits<T>())), pol), -2 * tools::digits<T>());
  108. }
  109. if(-0.5f == frexp(val, &expon))
  110. --expon; // reduce exponent when val is a power of two, and negative.
  111. T diff = ldexp(T(1), expon - tools::digits<T>());
  112. if(diff == 0)
  113. diff = detail::get_smallest_value<T>();
  114. return val + diff;
  115. }
  116. }
  117. template <class T, class Policy>
  118. inline typename tools::promote_args<T>::type float_next(const T& val, const Policy& pol)
  119. {
  120. typedef typename tools::promote_args<T>::type result_type;
  121. return detail::float_next_imp(static_cast<result_type>(val), pol);
  122. }
  123. #if 0 //def BOOST_MSVC
  124. //
  125. // We used to use ::_nextafter here, but doing so fails when using
  126. // the SSE2 registers if the FTZ or DAZ flags are set, so use our own
  127. // - albeit slower - code instead as at least that gives the correct answer.
  128. //
  129. template <class Policy>
  130. inline double float_next(const double& val, const Policy& pol)
  131. {
  132. static const char* function = "float_next<%1%>(%1%)";
  133. if(!(boost::math::isfinite)(val) && (val > 0))
  134. return policies::raise_domain_error<double>(
  135. function,
  136. "Argument must be finite, but got %1%", val, pol);
  137. if(val >= tools::max_value<double>())
  138. return policies::raise_overflow_error<double>(function, 0, pol);
  139. return ::_nextafter(val, tools::max_value<double>());
  140. }
  141. #endif
  142. template <class T>
  143. inline typename tools::promote_args<T>::type float_next(const T& val)
  144. {
  145. return float_next(val, policies::policy<>());
  146. }
  147. namespace detail{
  148. template <class T, class Policy>
  149. T float_prior_imp(const T& val, const Policy& pol)
  150. {
  151. BOOST_MATH_STD_USING
  152. int expon;
  153. static const char* function = "float_prior<%1%>(%1%)";
  154. int fpclass = (boost::math::fpclassify)(val);
  155. if((fpclass == FP_NAN) || (fpclass == FP_INFINITE))
  156. {
  157. if(val > 0)
  158. return tools::max_value<T>();
  159. return policies::raise_domain_error<T>(
  160. function,
  161. "Argument must be finite, but got %1%", val, pol);
  162. }
  163. if(val <= -tools::max_value<T>())
  164. return -policies::raise_overflow_error<T>(function, 0, pol);
  165. if(val == 0)
  166. return -detail::get_smallest_value<T>();
  167. if((fpclass != FP_SUBNORMAL) && (fpclass != FP_ZERO) && (fabs(val) < detail::get_min_shift_value<T>()) && (val != tools::min_value<T>()))
  168. {
  169. //
  170. // Special case: if the value of the least significant bit is a denorm, and the result
  171. // would not be a denorm, then shift the input, increment, and shift back.
  172. // This avoids issues with the Intel SSE2 registers when the FTZ or DAZ flags are set.
  173. //
  174. return ldexp(float_prior(T(ldexp(val, 2 * tools::digits<T>())), pol), -2 * tools::digits<T>());
  175. }
  176. T remain = frexp(val, &expon);
  177. if(remain == 0.5)
  178. --expon; // when val is a power of two we must reduce the exponent
  179. T diff = ldexp(T(1), expon - tools::digits<T>());
  180. if(diff == 0)
  181. diff = detail::get_smallest_value<T>();
  182. return val - diff;
  183. }
  184. }
  185. template <class T, class Policy>
  186. inline typename tools::promote_args<T>::type float_prior(const T& val, const Policy& pol)
  187. {
  188. typedef typename tools::promote_args<T>::type result_type;
  189. return detail::float_prior_imp(static_cast<result_type>(val), pol);
  190. }
  191. #if 0 //def BOOST_MSVC
  192. //
  193. // We used to use ::_nextafter here, but doing so fails when using
  194. // the SSE2 registers if the FTZ or DAZ flags are set, so use our own
  195. // - albeit slower - code instead as at least that gives the correct answer.
  196. //
  197. template <class Policy>
  198. inline double float_prior(const double& val, const Policy& pol)
  199. {
  200. static const char* function = "float_prior<%1%>(%1%)";
  201. if(!(boost::math::isfinite)(val) && (val < 0))
  202. return policies::raise_domain_error<double>(
  203. function,
  204. "Argument must be finite, but got %1%", val, pol);
  205. if(val <= -tools::max_value<double>())
  206. return -policies::raise_overflow_error<double>(function, 0, pol);
  207. return ::_nextafter(val, -tools::max_value<double>());
  208. }
  209. #endif
  210. template <class T>
  211. inline typename tools::promote_args<T>::type float_prior(const T& val)
  212. {
  213. return float_prior(val, policies::policy<>());
  214. }
  215. template <class T, class U, class Policy>
  216. inline typename tools::promote_args<T, U>::type nextafter(const T& val, const U& direction, const Policy& pol)
  217. {
  218. typedef typename tools::promote_args<T, U>::type result_type;
  219. return val < direction ? boost::math::float_next<result_type>(val, pol) : val == direction ? val : boost::math::float_prior<result_type>(val, pol);
  220. }
  221. template <class T, class U>
  222. inline typename tools::promote_args<T, U>::type nextafter(const T& val, const U& direction)
  223. {
  224. return nextafter(val, direction, policies::policy<>());
  225. }
  226. namespace detail{
  227. template <class T, class Policy>
  228. T float_distance_imp(const T& a, const T& b, const Policy& pol)
  229. {
  230. BOOST_MATH_STD_USING
  231. //
  232. // Error handling:
  233. //
  234. static const char* function = "float_distance<%1%>(%1%, %1%)";
  235. if(!(boost::math::isfinite)(a))
  236. return policies::raise_domain_error<T>(
  237. function,
  238. "Argument a must be finite, but got %1%", a, pol);
  239. if(!(boost::math::isfinite)(b))
  240. return policies::raise_domain_error<T>(
  241. function,
  242. "Argument b must be finite, but got %1%", b, pol);
  243. //
  244. // Special cases:
  245. //
  246. if(a > b)
  247. return -float_distance(b, a, pol);
  248. if(a == b)
  249. return 0;
  250. if(a == 0)
  251. return 1 + fabs(float_distance(static_cast<T>((b < 0) ? T(-detail::get_smallest_value<T>()) : detail::get_smallest_value<T>()), b, pol));
  252. if(b == 0)
  253. return 1 + fabs(float_distance(static_cast<T>((a < 0) ? T(-detail::get_smallest_value<T>()) : detail::get_smallest_value<T>()), a, pol));
  254. if(boost::math::sign(a) != boost::math::sign(b))
  255. return 2 + fabs(float_distance(static_cast<T>((b < 0) ? T(-detail::get_smallest_value<T>()) : detail::get_smallest_value<T>()), b, pol))
  256. + fabs(float_distance(static_cast<T>((a < 0) ? T(-detail::get_smallest_value<T>()) : detail::get_smallest_value<T>()), a, pol));
  257. //
  258. // By the time we get here, both a and b must have the same sign, we want
  259. // b > a and both postive for the following logic:
  260. //
  261. if(a < 0)
  262. return float_distance(static_cast<T>(-b), static_cast<T>(-a), pol);
  263. BOOST_ASSERT(a >= 0);
  264. BOOST_ASSERT(b >= a);
  265. int expon;
  266. //
  267. // Note that if a is a denorm then the usual formula fails
  268. // because we actually have fewer than tools::digits<T>()
  269. // significant bits in the representation:
  270. //
  271. frexp(((boost::math::fpclassify)(a) == FP_SUBNORMAL) ? tools::min_value<T>() : a, &expon);
  272. T upper = ldexp(T(1), expon);
  273. T result = 0;
  274. expon = tools::digits<T>() - expon;
  275. //
  276. // If b is greater than upper, then we *must* split the calculation
  277. // as the size of the ULP changes with each order of magnitude change:
  278. //
  279. if(b > upper)
  280. {
  281. result = float_distance(upper, b);
  282. }
  283. //
  284. // Use compensated double-double addition to avoid rounding
  285. // errors in the subtraction:
  286. //
  287. T mb, x, y, z;
  288. if(((boost::math::fpclassify)(a) == FP_SUBNORMAL) || (b - a < tools::min_value<T>()))
  289. {
  290. //
  291. // Special case - either one end of the range is a denormal, or else the difference is.
  292. // The regular code will fail if we're using the SSE2 registers on Intel and either
  293. // the FTZ or DAZ flags are set.
  294. //
  295. T a2 = ldexp(a, tools::digits<T>());
  296. T b2 = ldexp(b, tools::digits<T>());
  297. mb = -(std::min)(T(ldexp(upper, tools::digits<T>())), b2);
  298. x = a2 + mb;
  299. z = x - a2;
  300. y = (a2 - (x - z)) + (mb - z);
  301. expon -= tools::digits<T>();
  302. }
  303. else
  304. {
  305. mb = -(std::min)(upper, b);
  306. x = a + mb;
  307. z = x - a;
  308. y = (a - (x - z)) + (mb - z);
  309. }
  310. if(x < 0)
  311. {
  312. x = -x;
  313. y = -y;
  314. }
  315. result += ldexp(x, expon) + ldexp(y, expon);
  316. //
  317. // Result must be an integer:
  318. //
  319. BOOST_ASSERT(result == floor(result));
  320. return result;
  321. }
  322. }
  323. template <class T, class U, class Policy>
  324. inline typename tools::promote_args<T, U>::type float_distance(const T& a, const U& b, const Policy& pol)
  325. {
  326. typedef typename tools::promote_args<T, U>::type result_type;
  327. return detail::float_distance_imp(static_cast<result_type>(a), static_cast<result_type>(b), pol);
  328. }
  329. template <class T, class U>
  330. typename tools::promote_args<T, U>::type float_distance(const T& a, const U& b)
  331. {
  332. return boost::math::float_distance(a, b, policies::policy<>());
  333. }
  334. namespace detail{
  335. template <class T, class Policy>
  336. T float_advance_imp(T val, int distance, const Policy& pol)
  337. {
  338. BOOST_MATH_STD_USING
  339. //
  340. // Error handling:
  341. //
  342. static const char* function = "float_advance<%1%>(%1%, int)";
  343. int fpclass = (boost::math::fpclassify)(val);
  344. if((fpclass == FP_NAN) || (fpclass == FP_INFINITE))
  345. return policies::raise_domain_error<T>(
  346. function,
  347. "Argument val must be finite, but got %1%", val, pol);
  348. if(val < 0)
  349. return -float_advance(-val, -distance, pol);
  350. if(distance == 0)
  351. return val;
  352. if(distance == 1)
  353. return float_next(val, pol);
  354. if(distance == -1)
  355. return float_prior(val, pol);
  356. if(fabs(val) < detail::get_min_shift_value<T>())
  357. {
  358. //
  359. // Special case: if the value of the least significant bit is a denorm,
  360. // implement in terms of float_next/float_prior.
  361. // This avoids issues with the Intel SSE2 registers when the FTZ or DAZ flags are set.
  362. //
  363. if(distance > 0)
  364. {
  365. do{ val = float_next(val, pol); } while(--distance);
  366. }
  367. else
  368. {
  369. do{ val = float_prior(val, pol); } while(++distance);
  370. }
  371. return val;
  372. }
  373. int expon;
  374. frexp(val, &expon);
  375. T limit = ldexp((distance < 0 ? T(0.5f) : T(1)), expon);
  376. if(val <= tools::min_value<T>())
  377. {
  378. limit = sign(T(distance)) * tools::min_value<T>();
  379. }
  380. T limit_distance = float_distance(val, limit);
  381. while(fabs(limit_distance) < abs(distance))
  382. {
  383. distance -= itrunc(limit_distance);
  384. val = limit;
  385. if(distance < 0)
  386. {
  387. limit /= 2;
  388. expon--;
  389. }
  390. else
  391. {
  392. limit *= 2;
  393. expon++;
  394. }
  395. limit_distance = float_distance(val, limit);
  396. if(distance && (limit_distance == 0))
  397. {
  398. policies::raise_evaluation_error<T>(function, "Internal logic failed while trying to increment floating point value %1%: most likely your FPU is in non-IEEE conforming mode.", val, pol);
  399. }
  400. }
  401. if((0.5f == frexp(val, &expon)) && (distance < 0))
  402. --expon;
  403. T diff = 0;
  404. if(val != 0)
  405. diff = distance * ldexp(T(1), expon - tools::digits<T>());
  406. if(diff == 0)
  407. diff = distance * detail::get_smallest_value<T>();
  408. return val += diff;
  409. }
  410. }
  411. template <class T, class Policy>
  412. inline typename tools::promote_args<T>::type float_advance(T val, int distance, const Policy& pol)
  413. {
  414. typedef typename tools::promote_args<T>::type result_type;
  415. return detail::float_advance_imp(static_cast<result_type>(val), distance, pol);
  416. }
  417. template <class T>
  418. inline typename tools::promote_args<T>::type float_advance(const T& val, int distance)
  419. {
  420. return boost::math::float_advance(val, distance, policies::policy<>());
  421. }
  422. }} // namespaces
  423. #endif // BOOST_MATH_SPECIAL_NEXT_HPP