round.hpp 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  1. // (C) Copyright Matt Borland 2021.
  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_CCMATH_ROUND_HPP
  6. #define BOOST_MATH_CCMATH_ROUND_HPP
  7. #include <cmath>
  8. #include <type_traits>
  9. #include <stdexcept>
  10. #include <boost/math/tools/is_constant_evaluated.hpp>
  11. #include <boost/math/ccmath/abs.hpp>
  12. #include <boost/math/ccmath/isinf.hpp>
  13. #include <boost/math/ccmath/isnan.hpp>
  14. #include <boost/math/ccmath/modf.hpp>
  15. #include <boost/math/tools/is_standalone.hpp>
  16. #ifndef BOOST_MATH_STANDALONE
  17. #include <boost/config.hpp>
  18. #ifdef BOOST_NO_CXX17_IF_CONSTEXPR
  19. #error "The header <boost/math/norms.hpp> can only be used in C++17 and later."
  20. #endif
  21. #endif
  22. namespace boost::math::ccmath {
  23. namespace detail {
  24. // Computes the nearest integer value to arg (in floating-point format),
  25. // rounding halfway cases away from zero, regardless of the current rounding mode.
  26. template <typename T>
  27. inline constexpr T round_impl(T arg) noexcept
  28. {
  29. T iptr = 0;
  30. const T x = boost::math::ccmath::modf(arg, &iptr);
  31. constexpr T half = T(1)/2;
  32. if(x >= half && iptr > 0)
  33. {
  34. return iptr + 1;
  35. }
  36. else if(boost::math::ccmath::abs(x) >= half && iptr < 0)
  37. {
  38. return iptr - 1;
  39. }
  40. else
  41. {
  42. return iptr;
  43. }
  44. }
  45. template <typename ReturnType, typename T>
  46. inline constexpr ReturnType int_round_impl(T arg)
  47. {
  48. const T rounded_arg = round_impl(arg);
  49. if(rounded_arg > static_cast<T>((std::numeric_limits<ReturnType>::max)()))
  50. {
  51. if constexpr (std::is_same_v<ReturnType, long long>)
  52. {
  53. throw std::domain_error("Rounded value cannot be represented by a long long type without overflow");
  54. }
  55. else
  56. {
  57. throw std::domain_error("Rounded value cannot be represented by a long type without overflow");
  58. }
  59. }
  60. else
  61. {
  62. return static_cast<ReturnType>(rounded_arg);
  63. }
  64. }
  65. } // Namespace detail
  66. template <typename Real, std::enable_if_t<!std::is_integral_v<Real>, bool> = true>
  67. inline constexpr Real round(Real arg) noexcept
  68. {
  69. if(BOOST_MATH_IS_CONSTANT_EVALUATED(arg))
  70. {
  71. return boost::math::ccmath::abs(arg) == Real(0) ? arg :
  72. boost::math::ccmath::isinf(arg) ? arg :
  73. boost::math::ccmath::isnan(arg) ? arg :
  74. boost::math::ccmath::detail::round_impl(arg);
  75. }
  76. else
  77. {
  78. using std::round;
  79. return round(arg);
  80. }
  81. }
  82. template <typename Z, std::enable_if_t<std::is_integral_v<Z>, bool> = true>
  83. inline constexpr double round(Z arg) noexcept
  84. {
  85. return boost::math::ccmath::round(static_cast<double>(arg));
  86. }
  87. inline constexpr float roundf(float arg) noexcept
  88. {
  89. return boost::math::ccmath::round(arg);
  90. }
  91. #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
  92. inline constexpr long double roundl(long double arg) noexcept
  93. {
  94. return boost::math::ccmath::round(arg);
  95. }
  96. #endif
  97. template <typename Real, std::enable_if_t<!std::is_integral_v<Real>, bool> = true>
  98. inline constexpr long lround(Real arg)
  99. {
  100. if(BOOST_MATH_IS_CONSTANT_EVALUATED(arg))
  101. {
  102. return boost::math::ccmath::abs(arg) == Real(0) ? 0l :
  103. boost::math::ccmath::isinf(arg) ? 0l :
  104. boost::math::ccmath::isnan(arg) ? 0l :
  105. boost::math::ccmath::detail::int_round_impl<long>(arg);
  106. }
  107. else
  108. {
  109. using std::lround;
  110. return lround(arg);
  111. }
  112. }
  113. template <typename Z, std::enable_if_t<std::is_integral_v<Z>, bool> = true>
  114. inline constexpr long lround(Z arg)
  115. {
  116. return boost::math::ccmath::lround(static_cast<double>(arg));
  117. }
  118. inline constexpr long lroundf(float arg)
  119. {
  120. return boost::math::ccmath::lround(arg);
  121. }
  122. #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
  123. inline constexpr long lroundl(long double arg)
  124. {
  125. return boost::math::ccmath::lround(arg);
  126. }
  127. #endif
  128. template <typename Real, std::enable_if_t<!std::is_integral_v<Real>, bool> = true>
  129. inline constexpr long long llround(Real arg)
  130. {
  131. if(BOOST_MATH_IS_CONSTANT_EVALUATED(arg))
  132. {
  133. return boost::math::ccmath::abs(arg) == Real(0) ? 0ll :
  134. boost::math::ccmath::isinf(arg) ? 0ll :
  135. boost::math::ccmath::isnan(arg) ? 0ll :
  136. boost::math::ccmath::detail::int_round_impl<long long>(arg);
  137. }
  138. else
  139. {
  140. using std::llround;
  141. return llround(arg);
  142. }
  143. }
  144. template <typename Z, std::enable_if_t<std::is_integral_v<Z>, bool> = true>
  145. inline constexpr long llround(Z arg)
  146. {
  147. return boost::math::ccmath::llround(static_cast<double>(arg));
  148. }
  149. inline constexpr long long llroundf(float arg)
  150. {
  151. return boost::math::ccmath::llround(arg);
  152. }
  153. #ifndef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
  154. inline constexpr long long llroundl(long double arg)
  155. {
  156. return boost::math::ccmath::llround(arg);
  157. }
  158. #endif
  159. } // Namespaces
  160. #endif // BOOST_MATH_CCMATH_ROUND_HPP