fp_traits.hpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559
  1. // fp_traits.hpp
  2. #ifndef BOOST_MATH_FP_TRAITS_HPP
  3. #define BOOST_MATH_FP_TRAITS_HPP
  4. // Copyright (c) 2006 Johan Rade
  5. // Copyright (c) 2024 Matt Borland
  6. // Distributed under the Boost Software License, Version 1.0.
  7. // (See accompanying file LICENSE_1_0.txt
  8. // or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. /*
  10. To support old compilers, care has been taken to avoid partial template
  11. specialization and meta function forwarding.
  12. With these techniques, the code could be simplified.
  13. */
  14. #if defined(__vms) && defined(__DECCXX) && !__IEEE_FLOAT
  15. // The VAX floating point formats are used (for float and double)
  16. # define BOOST_FPCLASSIFY_VAX_FORMAT
  17. #endif
  18. #include <cstring>
  19. #include <cstdint>
  20. #include <limits>
  21. #include <type_traits>
  22. #include <boost/math/tools/config.hpp>
  23. #include <boost/math/tools/is_standalone.hpp>
  24. #include <boost/math/tools/assert.hpp>
  25. // Determine endianness
  26. #ifndef BOOST_MATH_STANDALONE
  27. #include <boost/predef/other/endian.h>
  28. #define BOOST_MATH_ENDIAN_BIG_BYTE BOOST_ENDIAN_BIG_BYTE
  29. #define BOOST_MATH_ENDIAN_LITTLE_BYTE BOOST_ENDIAN_LITTLE_BYTE
  30. #elif defined(_WIN32)
  31. #define BOOST_MATH_ENDIAN_BIG_BYTE 0
  32. #define BOOST_MATH_ENDIAN_LITTLE_BYTE 1
  33. #elif defined(__BYTE_ORDER__)
  34. #define BOOST_MATH_ENDIAN_BIG_BYTE (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
  35. #define BOOST_MATH_ENDIAN_LITTLE_BYTE (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
  36. #else
  37. #error Could not determine endian type. Please disable standalone mode, and file an issue at https://github.com/boostorg/math
  38. #endif // Determine endianness
  39. static_assert((BOOST_MATH_ENDIAN_BIG_BYTE || BOOST_MATH_ENDIAN_LITTLE_BYTE)
  40. && !(BOOST_MATH_ENDIAN_BIG_BYTE && BOOST_MATH_ENDIAN_LITTLE_BYTE),
  41. "Inconsistent endianness detected. Please disable standalone mode, and file an issue at https://github.com/boostorg/math");
  42. #ifdef BOOST_NO_STDC_NAMESPACE
  43. namespace std{ using ::memcpy; }
  44. #endif
  45. #ifndef FP_NORMAL
  46. #define FP_ZERO 0
  47. #define FP_NORMAL 1
  48. #define FP_INFINITE 2
  49. #define FP_NAN 3
  50. #define FP_SUBNORMAL 4
  51. #else
  52. #define BOOST_HAS_FPCLASSIFY
  53. #ifndef fpclassify
  54. # if (defined(__GLIBCPP__) || defined(__GLIBCXX__)) \
  55. && defined(_GLIBCXX_USE_C99_MATH) \
  56. && !(defined(_GLIBCXX_USE_C99_FP_MACROS_DYNAMIC) \
  57. && (_GLIBCXX_USE_C99_FP_MACROS_DYNAMIC != 0))
  58. # ifdef _STLP_VENDOR_CSTD
  59. # if _STLPORT_VERSION >= 0x520
  60. # define BOOST_FPCLASSIFY_PREFIX ::__std_alias::
  61. # else
  62. # define BOOST_FPCLASSIFY_PREFIX ::_STLP_VENDOR_CSTD::
  63. # endif
  64. # else
  65. # define BOOST_FPCLASSIFY_PREFIX ::std::
  66. # endif
  67. # else
  68. # undef BOOST_HAS_FPCLASSIFY
  69. # define BOOST_FPCLASSIFY_PREFIX
  70. # endif
  71. #elif (defined(__HP_aCC) && !defined(__hppa))
  72. // aCC 6 appears to do "#define fpclassify fpclassify" which messes us up a bit!
  73. # define BOOST_FPCLASSIFY_PREFIX ::
  74. #else
  75. # define BOOST_FPCLASSIFY_PREFIX
  76. #endif
  77. #ifdef __MINGW32__
  78. # undef BOOST_HAS_FPCLASSIFY
  79. #endif
  80. #endif
  81. //------------------------------------------------------------------------------
  82. namespace boost {
  83. namespace math {
  84. namespace detail {
  85. //------------------------------------------------------------------------------
  86. /*
  87. The following classes are used to tag the different methods that are used
  88. for floating point classification
  89. */
  90. struct native_tag {};
  91. template <bool has_limits>
  92. struct generic_tag {};
  93. struct ieee_tag {};
  94. struct ieee_copy_all_bits_tag : public ieee_tag {};
  95. struct ieee_copy_leading_bits_tag : public ieee_tag {};
  96. #ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
  97. //
  98. // These helper functions are used only when numeric_limits<>
  99. // members are not compile time constants:
  100. //
  101. inline bool is_generic_tag_false(const generic_tag<false>*)
  102. {
  103. return true;
  104. }
  105. inline bool is_generic_tag_false(const void*)
  106. {
  107. return false;
  108. }
  109. #endif
  110. //------------------------------------------------------------------------------
  111. /*
  112. Most processors support three different floating point precisions:
  113. single precision (32 bits), double precision (64 bits)
  114. and extended double precision (80 - 128 bits, depending on the processor)
  115. Note that the C++ type long double can be implemented
  116. both as double precision and extended double precision.
  117. */
  118. struct unknown_precision{};
  119. struct single_precision {};
  120. struct double_precision {};
  121. struct extended_double_precision {};
  122. // native_tag version --------------------------------------------------------------
  123. template<class T> struct fp_traits_native
  124. {
  125. typedef native_tag method;
  126. };
  127. // generic_tag version -------------------------------------------------------------
  128. template<class T, class U> struct fp_traits_non_native
  129. {
  130. #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
  131. typedef generic_tag<std::numeric_limits<T>::is_specialized> method;
  132. #else
  133. typedef generic_tag<false> method;
  134. #endif
  135. };
  136. // ieee_tag versions ---------------------------------------------------------------
  137. /*
  138. These specializations of fp_traits_non_native contain information needed
  139. to "parse" the binary representation of a floating point number.
  140. Typedef members:
  141. bits -- the target type when copying the leading bytes of a floating
  142. point number. It is a typedef for uint32_t or uint64_t.
  143. method -- tells us whether all bytes are copied or not.
  144. It is a typedef for ieee_copy_all_bits_tag or ieee_copy_leading_bits_tag.
  145. Static data members:
  146. sign, exponent, flag, significand -- bit masks that give the meaning of the
  147. bits in the leading bytes.
  148. Static function members:
  149. get_bits(), set_bits() -- provide access to the leading bytes.
  150. */
  151. // ieee_tag version, float (32 bits) -----------------------------------------------
  152. #ifndef BOOST_FPCLASSIFY_VAX_FORMAT
  153. template<> struct fp_traits_non_native<float, single_precision>
  154. {
  155. typedef ieee_copy_all_bits_tag method;
  156. BOOST_MATH_STATIC constexpr uint32_t sign = 0x80000000u;
  157. BOOST_MATH_STATIC constexpr uint32_t exponent = 0x7f800000;
  158. BOOST_MATH_STATIC constexpr uint32_t flag = 0x00000000;
  159. BOOST_MATH_STATIC constexpr uint32_t significand = 0x007fffff;
  160. typedef uint32_t bits;
  161. BOOST_MATH_GPU_ENABLED static void get_bits(float x, uint32_t& a) { std::memcpy(&a, &x, 4); }
  162. BOOST_MATH_GPU_ENABLED static void set_bits(float& x, uint32_t a) { std::memcpy(&x, &a, 4); }
  163. };
  164. // ieee_tag version, double (64 bits) ----------------------------------------------
  165. #if defined(BOOST_NO_INT64_T) || defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION) \
  166. || defined(BOOST_BORLANDC) || defined(__CODEGEAR__)
  167. template<> struct fp_traits_non_native<double, double_precision>
  168. {
  169. typedef ieee_copy_leading_bits_tag method;
  170. static constexpr uint32_t sign = 0x80000000u;
  171. static constexpr uint32_t exponent = 0x7ff00000;
  172. static constexpr uint32_t flag = 0;
  173. static constexpr uint32_t significand = 0x000fffff;
  174. typedef uint32_t bits;
  175. static void get_bits(double x, uint32_t& a)
  176. {
  177. std::memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
  178. }
  179. static void set_bits(double& x, uint32_t a)
  180. {
  181. std::memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
  182. }
  183. private:
  184. static constexpr int offset_ = BOOST_MATH_ENDIAN_BIG_BYTE ? 0 : 4;
  185. };
  186. //..............................................................................
  187. #else
  188. template<> struct fp_traits_non_native<double, double_precision>
  189. {
  190. typedef ieee_copy_all_bits_tag method;
  191. BOOST_MATH_STATIC constexpr uint64_t sign = static_cast<uint64_t>(0x80000000u) << 32;
  192. BOOST_MATH_STATIC constexpr uint64_t exponent = static_cast<uint64_t>(0x7ff00000) << 32;
  193. BOOST_MATH_STATIC constexpr uint64_t flag = 0;
  194. BOOST_MATH_STATIC constexpr uint64_t significand
  195. = (static_cast<uint64_t>(0x000fffff) << 32) + static_cast<uint64_t>(0xffffffffu);
  196. typedef uint64_t bits;
  197. BOOST_MATH_GPU_ENABLED static void get_bits(double x, uint64_t& a) { std::memcpy(&a, &x, 8); }
  198. BOOST_MATH_GPU_ENABLED static void set_bits(double& x, uint64_t a) { std::memcpy(&x, &a, 8); }
  199. };
  200. #endif
  201. #endif // #ifndef BOOST_FPCLASSIFY_VAX_FORMAT
  202. // long double (64 bits) -------------------------------------------------------
  203. #if defined(BOOST_NO_INT64_T) || defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION)\
  204. || defined(BOOST_BORLANDC) || defined(__CODEGEAR__) || (defined(__APPLE__) && defined(__aarch64__)) || defined(_MSC_VER)
  205. template<> struct fp_traits_non_native<long double, double_precision>
  206. {
  207. typedef ieee_copy_leading_bits_tag method;
  208. static constexpr uint32_t sign = 0x80000000u;
  209. static constexpr uint32_t exponent = 0x7ff00000;
  210. static constexpr uint32_t flag = 0;
  211. static constexpr uint32_t significand = 0x000fffff;
  212. typedef uint32_t bits;
  213. static void get_bits(long double x, uint32_t& a)
  214. {
  215. std::memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
  216. }
  217. static void set_bits(long double& x, uint32_t a)
  218. {
  219. std::memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
  220. }
  221. private:
  222. static constexpr int offset_ = BOOST_MATH_ENDIAN_BIG_BYTE ? 0 : 4;
  223. };
  224. // long double (>64 bits), x86 and x64 -----------------------------------------
  225. #elif defined(__i386) || defined(__i386__) || defined(_M_IX86) \
  226. || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) \
  227. || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64)
  228. // Intel extended double precision format (80 bits)
  229. template<>
  230. struct fp_traits_non_native<long double, extended_double_precision>
  231. {
  232. typedef ieee_copy_leading_bits_tag method;
  233. BOOST_MATH_STATIC constexpr uint32_t sign = 0x80000000u;
  234. BOOST_MATH_STATIC constexpr uint32_t exponent = 0x7fff0000;
  235. BOOST_MATH_STATIC constexpr uint32_t flag = 0x00008000;
  236. BOOST_MATH_STATIC constexpr uint32_t significand = 0x00007fff;
  237. typedef uint32_t bits;
  238. static void get_bits(long double x, uint32_t& a)
  239. {
  240. std::memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + 6, 4);
  241. }
  242. static void set_bits(long double& x, uint32_t a)
  243. {
  244. std::memcpy(reinterpret_cast<unsigned char*>(&x) + 6, &a, 4);
  245. }
  246. };
  247. // long double (>64 bits), Itanium ---------------------------------------------
  248. #elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64)
  249. // The floating point format is unknown at compile time
  250. // No template specialization is provided.
  251. // The generic_tag definition is used.
  252. // The Itanium supports both
  253. // the Intel extended double precision format (80 bits) and
  254. // the IEEE extended double precision format with 15 exponent bits (128 bits).
  255. #elif defined(__GNUC__) && (LDBL_MANT_DIG == 106)
  256. //
  257. // Define nothing here and fall though to generic_tag:
  258. // We have GCC's "double double" in effect, and any attempt
  259. // to handle it via bit-fiddling is pretty much doomed to fail...
  260. //
  261. // long double (>64 bits), PowerPC ---------------------------------------------
  262. #elif defined(__powerpc) || defined(__powerpc__) || defined(__POWERPC__) \
  263. || defined(__ppc) || defined(__ppc__) || defined(__PPC__)
  264. // PowerPC extended double precision format (128 bits)
  265. template<>
  266. struct fp_traits_non_native<long double, extended_double_precision>
  267. {
  268. typedef ieee_copy_leading_bits_tag method;
  269. BOOST_MATH_STATIC constexpr uint32_t sign = 0x80000000u;
  270. BOOST_MATH_STATIC constexpr uint32_t exponent = 0x7ff00000;
  271. BOOST_MATH_STATIC constexpr uint32_t flag = 0x00000000;
  272. BOOST_MATH_STATIC constexpr uint32_t significand = 0x000fffff;
  273. typedef uint32_t bits;
  274. static void get_bits(long double x, uint32_t& a)
  275. {
  276. std::memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
  277. }
  278. static void set_bits(long double& x, uint32_t a)
  279. {
  280. std::memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
  281. }
  282. private:
  283. BOOST_MATH_STATIC constexpr int offset_ = BOOST_MATH_ENDIAN_BIG_BYTE ? 0 : 12;
  284. };
  285. // long double (>64 bits), Motorola 68K ----------------------------------------
  286. #elif defined(__m68k) || defined(__m68k__) \
  287. || defined(__mc68000) || defined(__mc68000__) \
  288. // Motorola extended double precision format (96 bits)
  289. // It is the same format as the Intel extended double precision format,
  290. // except that 1) it is big-endian, 2) the 3rd and 4th byte are padding, and
  291. // 3) the flag bit is not set for infinity
  292. template<>
  293. struct fp_traits_non_native<long double, extended_double_precision>
  294. {
  295. typedef ieee_copy_leading_bits_tag method;
  296. BOOST_MATH_STATIC constexpr uint32_t sign = 0x80000000u;
  297. BOOST_MATH_STATIC constexpr uint32_t exponent = 0x7fff0000;
  298. BOOST_MATH_STATIC constexpr uint32_t flag = 0x00008000;
  299. BOOST_MATH_STATIC constexpr uint32_t significand = 0x00007fff;
  300. // copy 1st, 2nd, 5th and 6th byte. 3rd and 4th byte are padding.
  301. typedef uint32_t bits;
  302. static void get_bits(long double x, uint32_t& a)
  303. {
  304. std::memcpy(&a, &x, 2);
  305. std::memcpy(reinterpret_cast<unsigned char*>(&a) + 2,
  306. reinterpret_cast<const unsigned char*>(&x) + 4, 2);
  307. }
  308. static void set_bits(long double& x, uint32_t a)
  309. {
  310. std::memcpy(&x, &a, 2);
  311. std::memcpy(reinterpret_cast<unsigned char*>(&x) + 4,
  312. reinterpret_cast<const unsigned char*>(&a) + 2, 2);
  313. }
  314. };
  315. // long double (>64 bits), All other processors --------------------------------
  316. #else
  317. // IEEE extended double precision format with 15 exponent bits (128 bits)
  318. template<>
  319. struct fp_traits_non_native<long double, extended_double_precision>
  320. {
  321. typedef ieee_copy_leading_bits_tag method;
  322. BOOST_MATH_STATIC constexpr uint32_t sign = 0x80000000u;
  323. BOOST_MATH_STATIC constexpr uint32_t exponent = 0x7fff0000;
  324. BOOST_MATH_STATIC constexpr uint32_t flag = 0x00000000;
  325. BOOST_MATH_STATIC constexpr uint32_t significand = 0x0000ffff;
  326. typedef uint32_t bits;
  327. static void get_bits(long double x, uint32_t& a)
  328. {
  329. std::memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
  330. }
  331. static void set_bits(long double& x, uint32_t a)
  332. {
  333. std::memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
  334. }
  335. private:
  336. BOOST_MATH_STATIC constexpr int offset_ = BOOST_MATH_ENDIAN_BIG_BYTE ? 0 : 12;
  337. };
  338. #endif
  339. //------------------------------------------------------------------------------
  340. // size_to_precision is a type switch for converting a C++ floating point type
  341. // to the corresponding precision type.
  342. template<size_t n, bool fp> struct size_to_precision
  343. {
  344. typedef unknown_precision type;
  345. };
  346. template<> struct size_to_precision<4, true>
  347. {
  348. typedef single_precision type;
  349. };
  350. template<> struct size_to_precision<8, true>
  351. {
  352. typedef double_precision type;
  353. };
  354. template<> struct size_to_precision<10, true>
  355. {
  356. typedef extended_double_precision type;
  357. };
  358. template<> struct size_to_precision<12, true>
  359. {
  360. typedef extended_double_precision type;
  361. };
  362. template<> struct size_to_precision<16, true>
  363. {
  364. typedef extended_double_precision type;
  365. };
  366. //------------------------------------------------------------------------------
  367. //
  368. // Figure out whether to use native classification functions based on
  369. // whether T is a built in floating point type or not:
  370. //
  371. template <class T>
  372. struct select_native
  373. {
  374. typedef typename size_to_precision<sizeof(T), ::std::is_floating_point<T>::value>::type precision;
  375. typedef fp_traits_non_native<T, precision> type;
  376. };
  377. template<>
  378. struct select_native<float>
  379. {
  380. typedef fp_traits_native<float> type;
  381. };
  382. template<>
  383. struct select_native<double>
  384. {
  385. typedef fp_traits_native<double> type;
  386. };
  387. template<>
  388. struct select_native<long double>
  389. {
  390. typedef fp_traits_native<long double> type;
  391. };
  392. //------------------------------------------------------------------------------
  393. // fp_traits is a type switch that selects the right fp_traits_non_native
  394. #if (defined(BOOST_MATH_USE_C99) && !(defined(__GNUC__) && (__GNUC__ < 4))) \
  395. && !defined(__hpux) \
  396. && !defined(__DECCXX)\
  397. && !defined(__osf__) \
  398. && !defined(__SGI_STL_PORT) && !defined(_STLPORT_VERSION)\
  399. && !defined(__FAST_MATH__)\
  400. && !defined(BOOST_MATH_DISABLE_STD_FPCLASSIFY)\
  401. && !defined(__INTEL_COMPILER)\
  402. && !defined(sun)\
  403. && !defined(__VXWORKS__)\
  404. && !defined(BOOST_MATH_HAS_GPU_SUPPORT)
  405. # define BOOST_MATH_USE_STD_FPCLASSIFY
  406. #endif
  407. template<class T> struct fp_traits
  408. {
  409. typedef typename size_to_precision<sizeof(T), ::std::is_floating_point<T>::value>::type precision;
  410. #if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && !defined(BOOST_MATH_DISABLE_STD_FPCLASSIFY)
  411. typedef typename select_native<T>::type type;
  412. #else
  413. typedef fp_traits_non_native<T, precision> type;
  414. #endif
  415. typedef fp_traits_non_native<T, precision> sign_change_type;
  416. };
  417. //------------------------------------------------------------------------------
  418. } // namespace detail
  419. } // namespace math
  420. } // namespace boost
  421. #endif