File indexing completed on 2025-09-18 08:49:21
0001
0002
0003
0004
0005
0006 #ifndef BOOST_MATH_SPECIAL_NEXT_HPP
0007 #define BOOST_MATH_SPECIAL_NEXT_HPP
0008
0009 #ifdef _MSC_VER
0010 #pragma once
0011 #endif
0012
0013 #include <boost/math/tools/config.hpp>
0014
0015
0016 #ifndef BOOST_MATH_HAS_NVRTC
0017
0018 #include <boost/math/special_functions/math_fwd.hpp>
0019 #include <boost/math/policies/error_handling.hpp>
0020 #include <boost/math/special_functions/fpclassify.hpp>
0021 #include <boost/math/special_functions/sign.hpp>
0022 #include <boost/math/special_functions/trunc.hpp>
0023 #include <boost/math/tools/traits.hpp>
0024 #include <type_traits>
0025 #include <cfloat>
0026
0027
0028 #if !defined(_CRAYC) && !defined(__CUDACC__) && (!defined(__GNUC__) || (__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ > 3)))
0029 #if (defined(_M_IX86_FP) && (_M_IX86_FP >= 2)) || defined(__SSE2__)
0030 #include "xmmintrin.h"
0031 #define BOOST_MATH_CHECK_SSE2
0032 #endif
0033 #endif
0034
0035 namespace boost{ namespace math{
0036
0037 namespace concepts {
0038
0039 class real_concept;
0040 class std_real_concept;
0041
0042 }
0043
0044 namespace detail{
0045
0046 template <class T>
0047 struct has_hidden_guard_digits;
0048 template <>
0049 struct has_hidden_guard_digits<float> : public std::false_type {};
0050 template <>
0051 struct has_hidden_guard_digits<double> : public std::false_type {};
0052 template <>
0053 struct has_hidden_guard_digits<long double> : public std::false_type {};
0054 #ifdef BOOST_HAS_FLOAT128
0055 template <>
0056 struct has_hidden_guard_digits<__float128> : public std::false_type {};
0057 #endif
0058 template <>
0059 struct has_hidden_guard_digits<boost::math::concepts::real_concept> : public std::false_type {};
0060 template <>
0061 struct has_hidden_guard_digits<boost::math::concepts::std_real_concept> : public std::false_type {};
0062
0063 template <class T, bool b>
0064 struct has_hidden_guard_digits_10 : public std::false_type {};
0065 template <class T>
0066 struct has_hidden_guard_digits_10<T, true> : public std::integral_constant<bool, (std::numeric_limits<T>::digits10 != std::numeric_limits<T>::max_digits10)> {};
0067
0068 template <class T>
0069 struct has_hidden_guard_digits
0070 : public has_hidden_guard_digits_10<T,
0071 std::numeric_limits<T>::is_specialized
0072 && (std::numeric_limits<T>::radix == 10) >
0073 {};
0074
0075 template <class T>
0076 inline const T& normalize_value(const T& val, const std::false_type&) { return val; }
0077 template <class T>
0078 inline T normalize_value(const T& val, const std::true_type&)
0079 {
0080 static_assert(std::numeric_limits<T>::is_specialized, "Type T must be specialized.");
0081 static_assert(std::numeric_limits<T>::radix != 2, "Type T must be specialized.");
0082
0083 std::intmax_t shift = (std::intmax_t)std::numeric_limits<T>::digits - (std::intmax_t)ilogb(val) - 1;
0084 T result = scalbn(val, shift);
0085 result = round(result);
0086 return scalbn(result, -shift);
0087 }
0088
0089 template <class T>
0090 inline T get_smallest_value(std::true_type const&) {
0091 static_assert(std::numeric_limits<T>::is_specialized, "Type T must be specialized.");
0092
0093
0094
0095
0096
0097 static const T m = std::numeric_limits<T>::denorm_min();
0098 #ifdef BOOST_MATH_CHECK_SSE2
0099 return (_mm_getcsr() & (_MM_FLUSH_ZERO_ON | 0x40)) ? tools::min_value<T>() : m;
0100 #else
0101 return ((tools::min_value<T>() / 2) == 0) ? tools::min_value<T>() : m;
0102 #endif
0103 }
0104
0105 template <class T>
0106 inline T get_smallest_value(std::false_type const&)
0107 {
0108 return tools::min_value<T>();
0109 }
0110
0111 template <class T>
0112 inline T get_smallest_value()
0113 {
0114 return get_smallest_value<T>(std::integral_constant<bool, std::numeric_limits<T>::is_specialized>());
0115 }
0116
0117 template <class T>
0118 inline bool has_denorm_now() {
0119 return get_smallest_value<T>() < tools::min_value<T>();
0120 }
0121
0122
0123
0124
0125
0126 template <class T>
0127 T get_min_shift_value();
0128
0129 template <class T>
0130 struct min_shift_initializer
0131 {
0132 struct init
0133 {
0134 init()
0135 {
0136 do_init();
0137 }
0138 static void do_init()
0139 {
0140 get_min_shift_value<T>();
0141 }
0142 void force_instantiate()const{}
0143 };
0144 static const init initializer;
0145 static void force_instantiate()
0146 {
0147 initializer.force_instantiate();
0148 }
0149 };
0150
0151 template <class T>
0152 const typename min_shift_initializer<T>::init min_shift_initializer<T>::initializer;
0153
0154 template <class T>
0155 inline T calc_min_shifted(const std::true_type&)
0156 {
0157 BOOST_MATH_STD_USING
0158 return ldexp(tools::min_value<T>(), tools::digits<T>() + 1);
0159 }
0160 template <class T>
0161 inline T calc_min_shifted(const std::false_type&)
0162 {
0163 static_assert(std::numeric_limits<T>::is_specialized, "Type T must be specialized.");
0164 static_assert(std::numeric_limits<T>::radix != 2, "Type T must be specialized.");
0165
0166 return scalbn(tools::min_value<T>(), std::numeric_limits<T>::digits + 1);
0167 }
0168
0169
0170 template <class T>
0171 inline T get_min_shift_value()
0172 {
0173 static const T val = calc_min_shifted<T>(std::integral_constant<bool, !std::numeric_limits<T>::is_specialized || std::numeric_limits<T>::radix == 2>());
0174 min_shift_initializer<T>::force_instantiate();
0175
0176 return val;
0177 }
0178
0179 template <class T, bool b = boost::math::tools::detail::has_backend_type<T>::value>
0180 struct exponent_type
0181 {
0182 typedef int type;
0183 };
0184
0185 template <class T>
0186 struct exponent_type<T, true>
0187 {
0188 typedef typename T::backend_type::exponent_type type;
0189 };
0190
0191 template <class T, class Policy>
0192 T float_next_imp(const T& val, const std::true_type&, const Policy& pol)
0193 {
0194 typedef typename exponent_type<T>::type exponent_type;
0195
0196 BOOST_MATH_STD_USING
0197 exponent_type expon;
0198 static const char* function = "float_next<%1%>(%1%)";
0199
0200 int fpclass = (boost::math::fpclassify)(val);
0201
0202 if (fpclass == (int)FP_INFINITE)
0203 {
0204 if (val < 0)
0205 return -tools::max_value<T>();
0206 return val;
0207 }
0208 else if (fpclass == (int)FP_NAN)
0209 {
0210 return policies::raise_domain_error<T>(
0211 function,
0212 "Argument must be finite, but got %1%", val, pol);
0213 }
0214
0215 if(val >= tools::max_value<T>())
0216 return policies::raise_overflow_error<T>(function, nullptr, pol);
0217
0218 if(val == 0)
0219 return detail::get_smallest_value<T>();
0220
0221 if((fpclass != (int)FP_SUBNORMAL) && (fpclass != (int)FP_ZERO) && (fabs(val) < detail::get_min_shift_value<T>()) && (val != -tools::min_value<T>()))
0222 {
0223
0224
0225
0226
0227
0228 return ldexp(float_next(T(ldexp(val, 2 * tools::digits<T>())), pol), -2 * tools::digits<T>());
0229 }
0230
0231 if(-0.5f == frexp(val, &expon))
0232 --expon;
0233 T diff = ldexp(T(1), expon - tools::digits<T>());
0234 if(diff == 0)
0235 diff = detail::get_smallest_value<T>();
0236 return val + diff;
0237 }
0238
0239
0240
0241 template <class T, class Policy>
0242 T float_next_imp(const T& val, const std::false_type&, const Policy& pol)
0243 {
0244 typedef typename exponent_type<T>::type exponent_type;
0245
0246 static_assert(std::numeric_limits<T>::is_specialized, "Type T must be specialized.");
0247 static_assert(std::numeric_limits<T>::radix != 2, "Type T must be specialized.");
0248
0249 BOOST_MATH_STD_USING
0250 exponent_type expon;
0251 static const char* function = "float_next<%1%>(%1%)";
0252
0253 int fpclass = (boost::math::fpclassify)(val);
0254
0255 if (fpclass == (int)FP_INFINITE)
0256 {
0257 if (val < 0)
0258 return -tools::max_value<T>();
0259 return val;
0260 }
0261 else if (fpclass == (int)FP_NAN)
0262 {
0263 return policies::raise_domain_error<T>(
0264 function,
0265 "Argument must be finite, but got %1%", val, pol);
0266 }
0267
0268 if(val >= tools::max_value<T>())
0269 return policies::raise_overflow_error<T>(function, nullptr, pol);
0270
0271 if(val == 0)
0272 return detail::get_smallest_value<T>();
0273
0274 if((fpclass != (int)FP_SUBNORMAL) && (fpclass != (int)FP_ZERO) && (fabs(val) < detail::get_min_shift_value<T>()) && (val != -tools::min_value<T>()))
0275 {
0276
0277
0278
0279
0280
0281 return scalbn(float_next(T(scalbn(val, 2 * std::numeric_limits<T>::digits)), pol), -2 * std::numeric_limits<T>::digits);
0282 }
0283
0284 expon = 1 + ilogb(val);
0285 if(-1 == scalbn(val, -expon) * std::numeric_limits<T>::radix)
0286 --expon;
0287 T diff = scalbn(T(1), expon - std::numeric_limits<T>::digits);
0288 if(diff == 0)
0289 diff = detail::get_smallest_value<T>();
0290 return val + diff;
0291 }
0292
0293 }
0294
0295 template <class T, class Policy>
0296 inline typename tools::promote_args<T>::type float_next(const T& val, const Policy& pol)
0297 {
0298 typedef typename tools::promote_args<T>::type result_type;
0299 return detail::float_next_imp(detail::normalize_value(static_cast<result_type>(val), typename detail::has_hidden_guard_digits<result_type>::type()), std::integral_constant<bool, !std::numeric_limits<result_type>::is_specialized || (std::numeric_limits<result_type>::radix == 2)>(), pol);
0300 }
0301
0302 #if 0
0303
0304
0305
0306
0307
0308 template <class Policy>
0309 inline double float_next(const double& val, const Policy& pol)
0310 {
0311 static const char* function = "float_next<%1%>(%1%)";
0312
0313 if(!(boost::math::isfinite)(val) && (val > 0))
0314 return policies::raise_domain_error<double>(
0315 function,
0316 "Argument must be finite, but got %1%", val, pol);
0317
0318 if(val >= tools::max_value<double>())
0319 return policies::raise_overflow_error<double>(function, nullptr, pol);
0320
0321 return ::_nextafter(val, tools::max_value<double>());
0322 }
0323 #endif
0324
0325 template <class T>
0326 inline typename tools::promote_args<T>::type float_next(const T& val)
0327 {
0328 return float_next(val, policies::policy<>());
0329 }
0330
0331 namespace detail{
0332
0333 template <class T, class Policy>
0334 T float_prior_imp(const T& val, const std::true_type&, const Policy& pol)
0335 {
0336 typedef typename exponent_type<T>::type exponent_type;
0337
0338 BOOST_MATH_STD_USING
0339 exponent_type expon;
0340 static const char* function = "float_prior<%1%>(%1%)";
0341
0342 int fpclass = (boost::math::fpclassify)(val);
0343
0344 if (fpclass == (int)FP_INFINITE)
0345 {
0346 if (val > 0)
0347 return tools::max_value<T>();
0348 return val;
0349 }
0350 else if (fpclass == (int)FP_NAN)
0351 {
0352 return policies::raise_domain_error<T>(
0353 function,
0354 "Argument must be finite, but got %1%", val, pol);
0355 }
0356
0357 if(val <= -tools::max_value<T>())
0358 return -policies::raise_overflow_error<T>(function, nullptr, pol);
0359
0360 if(val == 0)
0361 return -detail::get_smallest_value<T>();
0362
0363 if((fpclass != (int)FP_SUBNORMAL) && (fpclass != (int)FP_ZERO) && (fabs(val) < detail::get_min_shift_value<T>()) && (val != tools::min_value<T>()))
0364 {
0365
0366
0367
0368
0369
0370 return ldexp(float_prior(T(ldexp(val, 2 * tools::digits<T>())), pol), -2 * tools::digits<T>());
0371 }
0372
0373 T remain = frexp(val, &expon);
0374 if(remain == 0.5f)
0375 --expon;
0376 T diff = ldexp(T(1), expon - tools::digits<T>());
0377 if(diff == 0)
0378 diff = detail::get_smallest_value<T>();
0379 return val - diff;
0380 }
0381
0382
0383
0384 template <class T, class Policy>
0385 T float_prior_imp(const T& val, const std::false_type&, const Policy& pol)
0386 {
0387 typedef typename exponent_type<T>::type exponent_type;
0388
0389 static_assert(std::numeric_limits<T>::is_specialized, "Type T must be specialized.");
0390 static_assert(std::numeric_limits<T>::radix != 2, "Type T must be specialized.");
0391
0392 BOOST_MATH_STD_USING
0393 exponent_type expon;
0394 static const char* function = "float_prior<%1%>(%1%)";
0395
0396 int fpclass = (boost::math::fpclassify)(val);
0397
0398 if (fpclass == (int)FP_INFINITE)
0399 {
0400 if (val > 0)
0401 return tools::max_value<T>();
0402 return val;
0403 }
0404 else if (fpclass == (int)FP_NAN)
0405 {
0406 return policies::raise_domain_error<T>(
0407 function,
0408 "Argument must be finite, but got %1%", val, pol);
0409 }
0410
0411 if(val <= -tools::max_value<T>())
0412 return -policies::raise_overflow_error<T>(function, nullptr, pol);
0413
0414 if(val == 0)
0415 return -detail::get_smallest_value<T>();
0416
0417 if((fpclass != (int)FP_SUBNORMAL) && (fpclass != (int)FP_ZERO) && (fabs(val) < detail::get_min_shift_value<T>()) && (val != tools::min_value<T>()))
0418 {
0419
0420
0421
0422
0423
0424 return scalbn(float_prior(T(scalbn(val, 2 * std::numeric_limits<T>::digits)), pol), -2 * std::numeric_limits<T>::digits);
0425 }
0426
0427 expon = 1 + ilogb(val);
0428 T remain = scalbn(val, -expon);
0429 if(remain * std::numeric_limits<T>::radix == 1)
0430 --expon;
0431 T diff = scalbn(T(1), expon - std::numeric_limits<T>::digits);
0432 if(diff == 0)
0433 diff = detail::get_smallest_value<T>();
0434 return val - diff;
0435 }
0436
0437 }
0438
0439 template <class T, class Policy>
0440 inline typename tools::promote_args<T>::type float_prior(const T& val, const Policy& pol)
0441 {
0442 typedef typename tools::promote_args<T>::type result_type;
0443 return detail::float_prior_imp(detail::normalize_value(static_cast<result_type>(val), typename detail::has_hidden_guard_digits<result_type>::type()), std::integral_constant<bool, !std::numeric_limits<result_type>::is_specialized || (std::numeric_limits<result_type>::radix == 2)>(), pol);
0444 }
0445
0446 #if 0
0447
0448
0449
0450
0451
0452 template <class Policy>
0453 inline double float_prior(const double& val, const Policy& pol)
0454 {
0455 static const char* function = "float_prior<%1%>(%1%)";
0456
0457 if(!(boost::math::isfinite)(val) && (val < 0))
0458 return policies::raise_domain_error<double>(
0459 function,
0460 "Argument must be finite, but got %1%", val, pol);
0461
0462 if(val <= -tools::max_value<double>())
0463 return -policies::raise_overflow_error<double>(function, nullptr, pol);
0464
0465 return ::_nextafter(val, -tools::max_value<double>());
0466 }
0467 #endif
0468
0469 template <class T>
0470 inline typename tools::promote_args<T>::type float_prior(const T& val)
0471 {
0472 return float_prior(val, policies::policy<>());
0473 }
0474
0475 template <class T, class U, class Policy>
0476 inline typename tools::promote_args<T, U>::type nextafter(const T& val, const U& direction, const Policy& pol)
0477 {
0478 typedef typename tools::promote_args<T, U>::type result_type;
0479 return val < direction ? boost::math::float_next<result_type>(val, pol) : val == direction ? val : boost::math::float_prior<result_type>(val, pol);
0480 }
0481
0482 template <class T, class U>
0483 inline typename tools::promote_args<T, U>::type nextafter(const T& val, const U& direction)
0484 {
0485 return nextafter(val, direction, policies::policy<>());
0486 }
0487
0488 namespace detail{
0489
0490 template <class T, class Policy>
0491 T float_distance_imp(const T& a, const T& b, const std::true_type&, const Policy& pol)
0492 {
0493 BOOST_MATH_STD_USING
0494
0495
0496
0497 static const char* function = "float_distance<%1%>(%1%, %1%)";
0498 if(!(boost::math::isfinite)(a))
0499 return policies::raise_domain_error<T>(
0500 function,
0501 "Argument a must be finite, but got %1%", a, pol);
0502 if(!(boost::math::isfinite)(b))
0503 return policies::raise_domain_error<T>(
0504 function,
0505 "Argument b must be finite, but got %1%", b, pol);
0506
0507
0508
0509 if(a > b)
0510 return -float_distance(b, a, pol);
0511 if(a == b)
0512 return T(0);
0513 if(a == 0)
0514 return 1 + fabs(float_distance(static_cast<T>((b < 0) ? T(-detail::get_smallest_value<T>()) : detail::get_smallest_value<T>()), b, pol));
0515 if(b == 0)
0516 return 1 + fabs(float_distance(static_cast<T>((a < 0) ? T(-detail::get_smallest_value<T>()) : detail::get_smallest_value<T>()), a, pol));
0517 if(boost::math::sign(a) != boost::math::sign(b))
0518 return 2 + fabs(float_distance(static_cast<T>((b < 0) ? T(-detail::get_smallest_value<T>()) : detail::get_smallest_value<T>()), b, pol))
0519 + fabs(float_distance(static_cast<T>((a < 0) ? T(-detail::get_smallest_value<T>()) : detail::get_smallest_value<T>()), a, pol));
0520
0521
0522
0523
0524 if(a < 0)
0525 return float_distance(static_cast<T>(-b), static_cast<T>(-a), pol);
0526
0527 BOOST_MATH_ASSERT(a >= 0);
0528 BOOST_MATH_ASSERT(b >= a);
0529
0530 int expon;
0531
0532
0533
0534
0535
0536 (void)frexp(((boost::math::fpclassify)(a) == (int)FP_SUBNORMAL) ? tools::min_value<T>() : a, &expon);
0537 T upper = ldexp(T(1), expon);
0538 T result = T(0);
0539
0540
0541
0542
0543 if(b > upper)
0544 {
0545 int expon2;
0546 (void)frexp(b, &expon2);
0547 T upper2 = ldexp(T(0.5), expon2);
0548 result = float_distance(upper2, b);
0549 result += (expon2 - expon - 1) * ldexp(T(1), tools::digits<T>() - 1);
0550 }
0551
0552
0553
0554
0555 expon = tools::digits<T>() - expon;
0556 T mb, x, y, z;
0557 if(((boost::math::fpclassify)(a) == (int)FP_SUBNORMAL) || (b - a < tools::min_value<T>()))
0558 {
0559
0560
0561
0562
0563
0564 T a2 = ldexp(a, tools::digits<T>());
0565 T b2 = ldexp(b, tools::digits<T>());
0566 mb = -(std::min)(T(ldexp(upper, tools::digits<T>())), b2);
0567 x = a2 + mb;
0568 z = x - a2;
0569 y = (a2 - (x - z)) + (mb - z);
0570
0571 expon -= tools::digits<T>();
0572 }
0573 else
0574 {
0575 mb = -(std::min)(upper, b);
0576 x = a + mb;
0577 z = x - a;
0578 y = (a - (x - z)) + (mb - z);
0579 }
0580 if(x < 0)
0581 {
0582 x = -x;
0583 y = -y;
0584 }
0585 result += ldexp(x, expon) + ldexp(y, expon);
0586
0587
0588
0589 BOOST_MATH_ASSERT(result == floor(result));
0590 return result;
0591 }
0592
0593
0594
0595 template <class T, class Policy>
0596 T float_distance_imp(const T& a, const T& b, const std::false_type&, const Policy& pol)
0597 {
0598 static_assert(std::numeric_limits<T>::is_specialized, "Type T must be specialized.");
0599 static_assert(std::numeric_limits<T>::radix != 2, "Type T must be specialized.");
0600
0601 BOOST_MATH_STD_USING
0602
0603
0604
0605 static const char* function = "float_distance<%1%>(%1%, %1%)";
0606 if(!(boost::math::isfinite)(a))
0607 return policies::raise_domain_error<T>(
0608 function,
0609 "Argument a must be finite, but got %1%", a, pol);
0610 if(!(boost::math::isfinite)(b))
0611 return policies::raise_domain_error<T>(
0612 function,
0613 "Argument b must be finite, but got %1%", b, pol);
0614
0615
0616
0617 if(a > b)
0618 return -float_distance(b, a, pol);
0619 if(a == b)
0620 return T(0);
0621 if(a == 0)
0622 return 1 + fabs(float_distance(static_cast<T>((b < 0) ? T(-detail::get_smallest_value<T>()) : detail::get_smallest_value<T>()), b, pol));
0623 if(b == 0)
0624 return 1 + fabs(float_distance(static_cast<T>((a < 0) ? T(-detail::get_smallest_value<T>()) : detail::get_smallest_value<T>()), a, pol));
0625 if(boost::math::sign(a) != boost::math::sign(b))
0626 return 2 + fabs(float_distance(static_cast<T>((b < 0) ? T(-detail::get_smallest_value<T>()) : detail::get_smallest_value<T>()), b, pol))
0627 + fabs(float_distance(static_cast<T>((a < 0) ? T(-detail::get_smallest_value<T>()) : detail::get_smallest_value<T>()), a, pol));
0628
0629
0630
0631
0632 if(a < 0)
0633 return float_distance(static_cast<T>(-b), static_cast<T>(-a), pol);
0634
0635 BOOST_MATH_ASSERT(a >= 0);
0636 BOOST_MATH_ASSERT(b >= a);
0637
0638 std::intmax_t expon;
0639
0640
0641
0642
0643
0644 expon = 1 + ilogb(((boost::math::fpclassify)(a) == (int)FP_SUBNORMAL) ? tools::min_value<T>() : a);
0645 T upper = scalbn(T(1), expon);
0646 T result = T(0);
0647
0648
0649
0650
0651 if(b > upper)
0652 {
0653 std::intmax_t expon2 = 1 + ilogb(b);
0654 T upper2 = scalbn(T(1), expon2 - 1);
0655 result = float_distance(upper2, b);
0656 result += (expon2 - expon - 1) * scalbn(T(1), std::numeric_limits<T>::digits - 1);
0657 }
0658
0659
0660
0661
0662 expon = std::numeric_limits<T>::digits - expon;
0663 T mb, x, y, z;
0664 if(((boost::math::fpclassify)(a) == (int)FP_SUBNORMAL) || (b - a < tools::min_value<T>()))
0665 {
0666
0667
0668
0669
0670
0671 T a2 = scalbn(a, std::numeric_limits<T>::digits);
0672 T b2 = scalbn(b, std::numeric_limits<T>::digits);
0673 mb = -(std::min)(T(scalbn(upper, std::numeric_limits<T>::digits)), b2);
0674 x = a2 + mb;
0675 z = x - a2;
0676 y = (a2 - (x - z)) + (mb - z);
0677
0678 expon -= std::numeric_limits<T>::digits;
0679 }
0680 else
0681 {
0682 mb = -(std::min)(upper, b);
0683 x = a + mb;
0684 z = x - a;
0685 y = (a - (x - z)) + (mb - z);
0686 }
0687 if(x < 0)
0688 {
0689 x = -x;
0690 y = -y;
0691 }
0692 result += scalbn(x, expon) + scalbn(y, expon);
0693
0694
0695
0696 BOOST_MATH_ASSERT(result == floor(result));
0697 return result;
0698 }
0699
0700 }
0701
0702 template <class T, class U, class Policy>
0703 inline typename tools::promote_args<T, U>::type float_distance(const T& a, const U& b, const Policy& pol)
0704 {
0705
0706
0707
0708 static_assert(
0709 (std::is_same<T, U>::value
0710 || (std::is_integral<T>::value && !std::is_integral<U>::value)
0711 || (!std::is_integral<T>::value && std::is_integral<U>::value)
0712 || (std::numeric_limits<T>::is_specialized && std::numeric_limits<U>::is_specialized
0713 && (std::numeric_limits<T>::digits == std::numeric_limits<U>::digits)
0714 && (std::numeric_limits<T>::radix == std::numeric_limits<U>::radix)
0715 && !std::numeric_limits<T>::is_integer && !std::numeric_limits<U>::is_integer)),
0716 "Float distance between two different floating point types is undefined.");
0717
0718 BOOST_MATH_IF_CONSTEXPR (!std::is_same<T, U>::value)
0719 {
0720 BOOST_MATH_IF_CONSTEXPR(std::is_integral<T>::value)
0721 {
0722 return float_distance(static_cast<U>(a), b, pol);
0723 }
0724 else
0725 {
0726 return float_distance(a, static_cast<T>(b), pol);
0727 }
0728 }
0729 else
0730 {
0731 typedef typename tools::promote_args<T, U>::type result_type;
0732 return detail::float_distance_imp(detail::normalize_value(static_cast<result_type>(a), typename detail::has_hidden_guard_digits<result_type>::type()), detail::normalize_value(static_cast<result_type>(b), typename detail::has_hidden_guard_digits<result_type>::type()), std::integral_constant<bool, !std::numeric_limits<result_type>::is_specialized || (std::numeric_limits<result_type>::radix == 2)>(), pol);
0733 }
0734 }
0735
0736 template <class T, class U>
0737 typename tools::promote_args<T, U>::type float_distance(const T& a, const U& b)
0738 {
0739 return boost::math::float_distance(a, b, policies::policy<>());
0740 }
0741
0742 namespace detail{
0743
0744 template <class T, class Policy>
0745 T float_advance_imp(T val, int distance, const std::true_type&, const Policy& pol)
0746 {
0747 BOOST_MATH_STD_USING
0748
0749
0750
0751 static const char* function = "float_advance<%1%>(%1%, int)";
0752
0753 int fpclass = (boost::math::fpclassify)(val);
0754
0755 if((fpclass == (int)FP_NAN) || (fpclass == (int)FP_INFINITE))
0756 return policies::raise_domain_error<T>(
0757 function,
0758 "Argument val must be finite, but got %1%", val, pol);
0759
0760 if(val < 0)
0761 return -float_advance(-val, -distance, pol);
0762 if(distance == 0)
0763 return val;
0764 if(distance == 1)
0765 return float_next(val, pol);
0766 if(distance == -1)
0767 return float_prior(val, pol);
0768
0769 if(fabs(val) < detail::get_min_shift_value<T>())
0770 {
0771
0772
0773
0774
0775
0776 if(distance > 0)
0777 {
0778 do{ val = float_next(val, pol); } while(--distance);
0779 }
0780 else
0781 {
0782 do{ val = float_prior(val, pol); } while(++distance);
0783 }
0784 return val;
0785 }
0786
0787 int expon;
0788 (void)frexp(val, &expon);
0789 T limit = ldexp((distance < 0 ? T(0.5f) : T(1)), expon);
0790 if(val <= tools::min_value<T>())
0791 {
0792 limit = sign(T(distance)) * tools::min_value<T>();
0793 }
0794 T limit_distance = float_distance(val, limit);
0795 while(fabs(limit_distance) < abs(distance))
0796 {
0797 distance -= itrunc(limit_distance);
0798 val = limit;
0799 if(distance < 0)
0800 {
0801 limit /= 2;
0802 expon--;
0803 }
0804 else
0805 {
0806 limit *= 2;
0807 expon++;
0808 }
0809 limit_distance = float_distance(val, limit);
0810 if(distance && (limit_distance == 0))
0811 {
0812 return 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);
0813 }
0814 }
0815 if((0.5f == frexp(val, &expon)) && (distance < 0))
0816 --expon;
0817 T diff = 0;
0818 if(val != 0)
0819 diff = distance * ldexp(T(1), expon - tools::digits<T>());
0820 if(diff == 0)
0821 diff = distance * detail::get_smallest_value<T>();
0822 return val += diff;
0823 }
0824
0825
0826
0827 template <class T, class Policy>
0828 T float_advance_imp(T val, int distance, const std::false_type&, const Policy& pol)
0829 {
0830 static_assert(std::numeric_limits<T>::is_specialized, "Type T must be specialized.");
0831 static_assert(std::numeric_limits<T>::radix != 2, "Type T must be specialized.");
0832
0833 BOOST_MATH_STD_USING
0834
0835
0836
0837 static const char* function = "float_advance<%1%>(%1%, int)";
0838
0839 int fpclass = (boost::math::fpclassify)(val);
0840
0841 if((fpclass == (int)FP_NAN) || (fpclass == (int)FP_INFINITE))
0842 return policies::raise_domain_error<T>(
0843 function,
0844 "Argument val must be finite, but got %1%", val, pol);
0845
0846 if(val < 0)
0847 return -float_advance(-val, -distance, pol);
0848 if(distance == 0)
0849 return val;
0850 if(distance == 1)
0851 return float_next(val, pol);
0852 if(distance == -1)
0853 return float_prior(val, pol);
0854
0855 if(fabs(val) < detail::get_min_shift_value<T>())
0856 {
0857
0858
0859
0860
0861
0862 if(distance > 0)
0863 {
0864 do{ val = float_next(val, pol); } while(--distance);
0865 }
0866 else
0867 {
0868 do{ val = float_prior(val, pol); } while(++distance);
0869 }
0870 return val;
0871 }
0872
0873 std::intmax_t expon = 1 + ilogb(val);
0874 T limit = scalbn(T(1), distance < 0 ? expon - 1 : expon);
0875 if(val <= tools::min_value<T>())
0876 {
0877 limit = sign(T(distance)) * tools::min_value<T>();
0878 }
0879 T limit_distance = float_distance(val, limit);
0880 while(fabs(limit_distance) < abs(distance))
0881 {
0882 distance -= itrunc(limit_distance);
0883 val = limit;
0884 if(distance < 0)
0885 {
0886 limit /= std::numeric_limits<T>::radix;
0887 expon--;
0888 }
0889 else
0890 {
0891 limit *= std::numeric_limits<T>::radix;
0892 expon++;
0893 }
0894 limit_distance = float_distance(val, limit);
0895 if(distance && (limit_distance == 0))
0896 {
0897 return 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);
0898 }
0899 }
0900
0901
0902
0903 T diff = 0;
0904 if(val != 0)
0905 diff = distance * scalbn(T(1), expon - std::numeric_limits<T>::digits);
0906 if(diff == 0)
0907 diff = distance * detail::get_smallest_value<T>();
0908 return val += diff;
0909 }
0910
0911 }
0912
0913 template <class T, class Policy>
0914 inline typename tools::promote_args<T>::type float_advance(T val, int distance, const Policy& pol)
0915 {
0916 typedef typename tools::promote_args<T>::type result_type;
0917 return detail::float_advance_imp(detail::normalize_value(static_cast<result_type>(val), typename detail::has_hidden_guard_digits<result_type>::type()), distance, std::integral_constant<bool, !std::numeric_limits<result_type>::is_specialized || (std::numeric_limits<result_type>::radix == 2)>(), pol);
0918 }
0919
0920 template <class T>
0921 inline typename tools::promote_args<T>::type float_advance(const T& val, int distance)
0922 {
0923 return boost::math::float_advance(val, distance, policies::policy<>());
0924 }
0925
0926 }}
0927
0928 #endif
0929
0930 #endif