signbit.hpp 6.2 KB

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