signbit.hpp 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. // (C) Copyright Matt Borland 2022.
  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_SIGNBIT_HPP
  6. #define BOOST_MATH_CCMATH_SIGNBIT_HPP
  7. #include <boost/math/ccmath/detail/config.hpp>
  8. #ifdef BOOST_MATH_NO_CCMATH
  9. #error "The header <boost/math/signbit.hpp> can only be used in C++17 and later."
  10. #endif
  11. #include <cstdint>
  12. #include <boost/math/tools/assert.hpp>
  13. #include <boost/math/special_functions/detail/fp_traits.hpp>
  14. #include <boost/math/ccmath/isnan.hpp>
  15. #include <boost/math/ccmath/abs.hpp>
  16. #ifdef __has_include
  17. # if __has_include(<bit>)
  18. # include <bit>
  19. # if __cpp_lib_bit_cast >= 201806L
  20. # define BOOST_MATH_BIT_CAST(T, x) std::bit_cast<T>(x)
  21. # endif
  22. # elif defined(__has_builtin)
  23. # if __has_builtin(__builtin_bit_cast)
  24. # define BOOST_MATH_BIT_CAST(T, x) __builtin_bit_cast(T, x)
  25. # endif
  26. # endif
  27. #endif
  28. /*
  29. The following error is given using Apple Clang version 13.1.6, and Clang 13, and 14 on Ubuntu 22.04.01
  30. TODO: Remove the following undef when Apple Clang supports
  31. ccmath_signbit_test.cpp:32:19: error: static_assert expression is not an integral constant expression
  32. static_assert(boost::math::ccmath::signbit(T(-1)) == true);
  33. ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  34. ../../../boost/math/ccmath/signbit.hpp:62:24: note: constexpr bit_cast involving bit-field is not yet supported
  35. const auto u = BOOST_MATH_BIT_CAST(float_bits, arg);
  36. ^
  37. ../../../boost/math/ccmath/signbit.hpp:20:37: note: expanded from macro 'BOOST_MATH_BIT_CAST'
  38. # define BOOST_MATH_BIT_CAST(T, x) __builtin_bit_cast(T, x)
  39. ^
  40. */
  41. #if defined(__clang__) && defined(BOOST_MATH_BIT_CAST)
  42. # undef BOOST_MATH_BIT_CAST
  43. #endif
  44. namespace boost::math::ccmath {
  45. namespace detail {
  46. #ifdef BOOST_MATH_BIT_CAST
  47. struct IEEEf2bits
  48. {
  49. #if BOOST_MATH_ENDIAN_LITTLE_BYTE
  50. std::uint32_t mantissa : 23;
  51. std::uint32_t exponent : 8;
  52. std::uint32_t sign : 1;
  53. #else // Big endian
  54. std::uint32_t sign : 1;
  55. std::uint32_t exponent : 8;
  56. std::uint32_t mantissa : 23;
  57. #endif
  58. };
  59. struct IEEEd2bits
  60. {
  61. #if BOOST_MATH_ENDIAN_LITTLE_BYTE
  62. std::uint32_t mantissa_l : 32;
  63. std::uint32_t mantissa_h : 20;
  64. std::uint32_t exponent : 11;
  65. std::uint32_t sign : 1;
  66. #else // Big endian
  67. std::uint32_t sign : 1;
  68. std::uint32_t exponent : 11;
  69. std::uint32_t mantissa_h : 20;
  70. std::uint32_t mantissa_l : 32;
  71. #endif
  72. };
  73. // 80 bit long double
  74. #if LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384
  75. struct IEEEl2bits
  76. {
  77. #if BOOST_MATH_ENDIAN_LITTLE_BYTE
  78. std::uint32_t mantissa_l : 32;
  79. std::uint32_t mantissa_h : 32;
  80. std::uint32_t exponent : 15;
  81. std::uint32_t sign : 1;
  82. std::uint32_t pad : 32;
  83. #else // Big endian
  84. std::uint32_t pad : 32;
  85. std::uint32_t sign : 1;
  86. std::uint32_t exponent : 15;
  87. std::uint32_t mantissa_h : 32;
  88. std::uint32_t mantissa_l : 32;
  89. #endif
  90. };
  91. // 128 bit long double
  92. #elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384
  93. struct IEEEl2bits
  94. {
  95. #if BOOST_MATH_ENDIAN_LITTLE_BYTE
  96. std::uint64_t mantissa_l : 64;
  97. std::uint64_t mantissa_h : 48;
  98. std::uint32_t exponent : 15;
  99. std::uint32_t sign : 1;
  100. #else // Big endian
  101. std::uint32_t sign : 1;
  102. std::uint32_t exponent : 15;
  103. std::uint64_t mantissa_h : 48;
  104. std::uint64_t mantissa_l : 64;
  105. #endif
  106. };
  107. // 64 bit long double (double == long double on ARM)
  108. #elif LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
  109. struct IEEEl2bits
  110. {
  111. #if BOOST_MATH_ENDIAN_LITTLE_BYTE
  112. std::uint32_t mantissa_l : 32;
  113. std::uint32_t mantissa_h : 20;
  114. std::uint32_t exponent : 11;
  115. std::uint32_t sign : 1;
  116. #else // Big endian
  117. std::uint32_t sign : 1;
  118. std::uint32_t exponent : 11;
  119. std::uint32_t mantissa_h : 20;
  120. std::uint32_t mantissa_l : 32;
  121. #endif
  122. };
  123. #else // Unsupported long double representation
  124. # define BOOST_MATH_UNSUPPORTED_LONG_DOUBLE
  125. #endif
  126. template <typename T>
  127. constexpr bool signbit_impl(T arg)
  128. {
  129. if constexpr (std::is_same_v<T, float>)
  130. {
  131. const auto u = BOOST_MATH_BIT_CAST(IEEEf2bits, arg);
  132. return u.sign;
  133. }
  134. else if constexpr (std::is_same_v<T, double>)
  135. {
  136. const auto u = BOOST_MATH_BIT_CAST(IEEEd2bits, arg);
  137. return u.sign;
  138. }
  139. #ifndef BOOST_MATH_UNSUPPORTED_LONG_DOUBLE
  140. else if constexpr (std::is_same_v<T, long double>)
  141. {
  142. const auto u = BOOST_MATH_BIT_CAST(IEEEl2bits, arg);
  143. return u.sign;
  144. }
  145. #endif
  146. else
  147. {
  148. BOOST_MATH_ASSERT_MSG(!boost::math::ccmath::isnan(arg), "NAN is not supported with this type or platform");
  149. BOOST_MATH_ASSERT_MSG(boost::math::ccmath::abs(arg) != 0, "Signed 0 is not support with this type or platform");
  150. return arg < static_cast<T>(0);
  151. }
  152. }
  153. #else
  154. // Typical implementations of signbit involve type punning via union and manipulating
  155. // overflow (see libc++ or musl). Neither of these are allowed in constexpr contexts
  156. // (technically type punning via union in general is UB in c++ but well defined in C)
  157. // therefore we static assert these cases.
  158. template <typename T>
  159. constexpr bool signbit_impl(T arg)
  160. {
  161. BOOST_MATH_ASSERT_MSG(!boost::math::ccmath::isnan(arg), "NAN is not supported without __builtin_bit_cast or std::bit_cast");
  162. BOOST_MATH_ASSERT_MSG(boost::math::ccmath::abs(arg) != 0, "Signed 0 is not support without __builtin_bit_cast or std::bit_cast");
  163. return arg < static_cast<T>(0);
  164. }
  165. #endif
  166. }
  167. // Return value: true if arg is negative, false if arg is 0, NAN, or positive
  168. template <typename Real, std::enable_if_t<!std::is_integral_v<Real>, bool> = true>
  169. constexpr bool signbit(Real arg)
  170. {
  171. if (BOOST_MATH_IS_CONSTANT_EVALUATED(arg))
  172. {
  173. return boost::math::ccmath::detail::signbit_impl(arg);
  174. }
  175. else
  176. {
  177. using std::signbit;
  178. return signbit(arg);
  179. }
  180. }
  181. template <typename Z, std::enable_if_t<std::is_integral_v<Z>, bool> = true>
  182. constexpr bool signbit(Z arg)
  183. {
  184. return boost::math::ccmath::signbit(static_cast<double>(arg));
  185. }
  186. } // Namespaces
  187. #endif // BOOST_MATH_CCMATH_SIGNBIT_HPP