round.hpp 4.7 KB

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