fp_traits.hpp 18 KB

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