581 lines · plain
1// fp_traits.hpp2 3#ifndef BOOST_MATH_FP_TRAITS_HPP4#define BOOST_MATH_FP_TRAITS_HPP5 6// Copyright (c) 2006 Johan Rade7// Copyright (c) 2024 Matt Borland8 9// Distributed under the Boost Software License, Version 1.0.10// (See accompanying file LICENSE_1_0.txt11// or copy at http://www.boost.org/LICENSE_1_0.txt)12 13/*14To support old compilers, care has been taken to avoid partial template15specialization and meta function forwarding.16With these techniques, the code could be simplified.17*/18 19#if defined(__vms) && defined(__DECCXX) && !__IEEE_FLOAT20// The VAX floating point formats are used (for float and double)21# define BOOST_FPCLASSIFY_VAX_FORMAT22#endif23 24#include <cstring>25#include <cstdint>26#include <limits>27#include <type_traits>28#include <boost/math/tools/config.hpp>29#include <boost/math/tools/is_standalone.hpp>30#include <boost/math/tools/assert.hpp>31 32// Determine endianness33#ifndef BOOST_MATH_STANDALONE34 35#include <boost/predef/other/endian.h>36#define BOOST_MATH_ENDIAN_BIG_BYTE BOOST_ENDIAN_BIG_BYTE37#define BOOST_MATH_ENDIAN_LITTLE_BYTE BOOST_ENDIAN_LITTLE_BYTE38 39#elif defined(_WIN32)40 41#define BOOST_MATH_ENDIAN_BIG_BYTE 042#define BOOST_MATH_ENDIAN_LITTLE_BYTE 143 44#elif defined(__BYTE_ORDER__)45 46#define BOOST_MATH_ENDIAN_BIG_BYTE (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)47#define BOOST_MATH_ENDIAN_LITTLE_BYTE (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)48 49#else50#error Could not determine endian type. Please disable standalone mode, and file an issue at https://github.com/boostorg/math51#endif // Determine endianness52 53static_assert((BOOST_MATH_ENDIAN_BIG_BYTE || BOOST_MATH_ENDIAN_LITTLE_BYTE)54 && !(BOOST_MATH_ENDIAN_BIG_BYTE && BOOST_MATH_ENDIAN_LITTLE_BYTE),55 "Inconsistent endianness detected. Please disable standalone mode, and file an issue at https://github.com/boostorg/math");56 57#ifdef BOOST_NO_STDC_NAMESPACE58 namespace std{ using ::memcpy; }59#endif60 61#ifndef FP_NORMAL62 63#define FP_ZERO 064#define FP_NORMAL 165#define FP_INFINITE 266#define FP_NAN 367#define FP_SUBNORMAL 468 69#else70 71#define BOOST_HAS_FPCLASSIFY72 73#ifndef fpclassify74# if (defined(__GLIBCPP__) || defined(__GLIBCXX__)) \75 && defined(_GLIBCXX_USE_C99_MATH) \76 && !(defined(_GLIBCXX_USE_C99_FP_MACROS_DYNAMIC) \77 && (_GLIBCXX_USE_C99_FP_MACROS_DYNAMIC != 0))78# ifdef _STLP_VENDOR_CSTD79# if _STLPORT_VERSION >= 0x52080# define BOOST_FPCLASSIFY_PREFIX ::__std_alias::81# else82# define BOOST_FPCLASSIFY_PREFIX ::_STLP_VENDOR_CSTD::83# endif84# else85# define BOOST_FPCLASSIFY_PREFIX ::std::86# endif87# else88# undef BOOST_HAS_FPCLASSIFY89# define BOOST_FPCLASSIFY_PREFIX90# endif91#elif (defined(__HP_aCC) && !defined(__hppa))92// aCC 6 appears to do "#define fpclassify fpclassify" which messes us up a bit!93# define BOOST_FPCLASSIFY_PREFIX ::94#else95# define BOOST_FPCLASSIFY_PREFIX96#endif97 98#ifdef __MINGW32__99# undef BOOST_HAS_FPCLASSIFY100#endif101 102#endif103 104 105//------------------------------------------------------------------------------106 107namespace boost {108namespace math {109namespace detail {110 111//------------------------------------------------------------------------------112 113/*114The following classes are used to tag the different methods that are used115for floating point classification116*/117 118struct native_tag {};119template <bool has_limits>120struct generic_tag {};121struct ieee_tag {};122struct ieee_copy_all_bits_tag : public ieee_tag {};123struct ieee_copy_leading_bits_tag : public ieee_tag {};124 125#ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS126//127// These helper functions are used only when numeric_limits<>128// members are not compile time constants:129//130inline bool is_generic_tag_false(const generic_tag<false>*)131{132 return true;133}134inline bool is_generic_tag_false(const void*)135{136 return false;137}138#endif139 140//------------------------------------------------------------------------------141 142/*143Most processors support three different floating point precisions:144single precision (32 bits), double precision (64 bits)145and extended double precision (80 - 128 bits, depending on the processor)146 147Note that the C++ type long double can be implemented148both as double precision and extended double precision.149*/150 151struct unknown_precision{};152struct single_precision {};153struct double_precision {};154struct extended_double_precision {};155 156// native_tag version --------------------------------------------------------------157 158template<class T> struct fp_traits_native159{160 typedef native_tag method;161};162 163// generic_tag version -------------------------------------------------------------164 165template<class T, class U> struct fp_traits_non_native166{167#ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS168 typedef generic_tag<std::numeric_limits<T>::is_specialized> method;169#else170 typedef generic_tag<false> method;171#endif172};173 174// ieee_tag versions ---------------------------------------------------------------175 176/*177These specializations of fp_traits_non_native contain information needed178to "parse" the binary representation of a floating point number.179 180Typedef members:181 182 bits -- the target type when copying the leading bytes of a floating183 point number. It is a typedef for uint32_t or uint64_t.184 185 method -- tells us whether all bytes are copied or not.186 It is a typedef for ieee_copy_all_bits_tag or ieee_copy_leading_bits_tag.187 188Static data members:189 190 sign, exponent, flag, significand -- bit masks that give the meaning of the191 bits in the leading bytes.192 193Static function members:194 195 get_bits(), set_bits() -- provide access to the leading bytes.196 197*/198 199// ieee_tag version, float (32 bits) -----------------------------------------------200 201#ifndef BOOST_FPCLASSIFY_VAX_FORMAT202 203template<> struct fp_traits_non_native<float, single_precision>204{205 typedef ieee_copy_all_bits_tag method;206 207 BOOST_MATH_STATIC constexpr uint32_t sign = 0x80000000u;208 BOOST_MATH_STATIC constexpr uint32_t exponent = 0x7f800000;209 BOOST_MATH_STATIC constexpr uint32_t flag = 0x00000000;210 BOOST_MATH_STATIC constexpr uint32_t significand = 0x007fffff;211 212 typedef uint32_t bits;213 BOOST_MATH_GPU_ENABLED static void get_bits(float x, uint32_t& a) { std::memcpy(&a, &x, 4); }214 BOOST_MATH_GPU_ENABLED static void set_bits(float& x, uint32_t a) { std::memcpy(&x, &a, 4); }215};216 217// ieee_tag version, double (64 bits) ----------------------------------------------218 219#if defined(BOOST_NO_INT64_T) || defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION) \220 || defined(BOOST_BORLANDC) || defined(__CODEGEAR__)221 222template<> struct fp_traits_non_native<double, double_precision>223{224 typedef ieee_copy_leading_bits_tag method;225 226 static constexpr uint32_t sign = 0x80000000u;227 static constexpr uint32_t exponent = 0x7ff00000;228 static constexpr uint32_t flag = 0;229 static constexpr uint32_t significand = 0x000fffff;230 231 typedef uint32_t bits;232 233 static void get_bits(double x, uint32_t& a)234 {235 std::memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);236 }237 238 static void set_bits(double& x, uint32_t a)239 {240 std::memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);241 }242 243private:244 static constexpr int offset_ = BOOST_MATH_ENDIAN_BIG_BYTE ? 0 : 4;245};246 247//..............................................................................248 249#else250 251template<> struct fp_traits_non_native<double, double_precision>252{253 typedef ieee_copy_all_bits_tag method;254 255 BOOST_MATH_STATIC constexpr uint64_t sign = static_cast<uint64_t>(0x80000000u) << 32;256 BOOST_MATH_STATIC constexpr uint64_t exponent = static_cast<uint64_t>(0x7ff00000) << 32;257 BOOST_MATH_STATIC constexpr uint64_t flag = 0;258 BOOST_MATH_STATIC constexpr uint64_t significand259 = (static_cast<uint64_t>(0x000fffff) << 32) + static_cast<uint64_t>(0xffffffffu);260 261 typedef uint64_t bits;262 BOOST_MATH_GPU_ENABLED static void get_bits(double x, uint64_t& a) { std::memcpy(&a, &x, 8); }263 BOOST_MATH_GPU_ENABLED static void set_bits(double& x, uint64_t a) { std::memcpy(&x, &a, 8); }264};265 266#endif267 268#endif // #ifndef BOOST_FPCLASSIFY_VAX_FORMAT269 270// long double (64 bits) -------------------------------------------------------271 272#if defined(BOOST_NO_INT64_T) || defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION)\273 || defined(BOOST_BORLANDC) || defined(__CODEGEAR__)274 275template<> struct fp_traits_non_native<long double, double_precision>276{277 typedef ieee_copy_leading_bits_tag method;278 279 static constexpr uint32_t sign = 0x80000000u;280 static constexpr uint32_t exponent = 0x7ff00000;281 static constexpr uint32_t flag = 0;282 static constexpr uint32_t significand = 0x000fffff;283 284 typedef uint32_t bits;285 286 static void get_bits(long double x, uint32_t& a)287 {288 std::memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);289 }290 291 static void set_bits(long double& x, uint32_t a)292 {293 std::memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);294 }295 296private:297 static constexpr int offset_ = BOOST_MATH_ENDIAN_BIG_BYTE ? 0 : 4;298};299 300//..............................................................................301 302#else303 304template<> struct fp_traits_non_native<long double, double_precision>305{306 typedef ieee_copy_all_bits_tag method;307 308 static const uint64_t sign = static_cast<uint64_t>(0x80000000u) << 32;309 static const uint64_t exponent = static_cast<uint64_t>(0x7ff00000) << 32;310 static const uint64_t flag = 0;311 static const uint64_t significand312 = (static_cast<uint64_t>(0x000fffff) << 32) + static_cast<uint64_t>(0xffffffffu);313 314 typedef uint64_t bits;315 static void get_bits(long double x, uint64_t& a) { std::memcpy(&a, &x, 8); }316 static void set_bits(long double& x, uint64_t a) { std::memcpy(&x, &a, 8); }317};318 319#endif320 321 322// long double (>64 bits), x86 and x64 -----------------------------------------323 324#if defined(__i386) || defined(__i386__) || defined(_M_IX86) \325 || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) \326 || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64)327 328// Intel extended double precision format (80 bits)329 330template<>331struct fp_traits_non_native<long double, extended_double_precision>332{333 typedef ieee_copy_leading_bits_tag method;334 335 BOOST_MATH_STATIC constexpr uint32_t sign = 0x80000000u;336 BOOST_MATH_STATIC constexpr uint32_t exponent = 0x7fff0000;337 BOOST_MATH_STATIC constexpr uint32_t flag = 0x00008000;338 BOOST_MATH_STATIC constexpr uint32_t significand = 0x00007fff;339 340 typedef uint32_t bits;341 342 static void get_bits(long double x, uint32_t& a)343 {344 std::memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + 6, 4);345 }346 347 static void set_bits(long double& x, uint32_t a)348 {349 std::memcpy(reinterpret_cast<unsigned char*>(&x) + 6, &a, 4);350 }351};352 353 354// long double (>64 bits), Itanium ---------------------------------------------355 356#elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64)357 358// The floating point format is unknown at compile time359// No template specialization is provided.360// The generic_tag definition is used.361 362// The Itanium supports both363// the Intel extended double precision format (80 bits) and364// the IEEE extended double precision format with 15 exponent bits (128 bits).365 366#elif defined(__GNUC__) && (LDBL_MANT_DIG == 106)367 368//369// Define nothing here and fall though to generic_tag:370// We have GCC's "double double" in effect, and any attempt371// to handle it via bit-fiddling is pretty much doomed to fail...372//373 374// long double (>64 bits), PowerPC ---------------------------------------------375 376#elif defined(__powerpc) || defined(__powerpc__) || defined(__POWERPC__) \377 || defined(__ppc) || defined(__ppc__) || defined(__PPC__)378 379// PowerPC extended double precision format (128 bits)380 381template<>382struct fp_traits_non_native<long double, extended_double_precision>383{384 typedef ieee_copy_leading_bits_tag method;385 386 BOOST_MATH_STATIC constexpr uint32_t sign = 0x80000000u;387 BOOST_MATH_STATIC constexpr uint32_t exponent = 0x7ff00000;388 BOOST_MATH_STATIC constexpr uint32_t flag = 0x00000000;389 BOOST_MATH_STATIC constexpr uint32_t significand = 0x000fffff;390 391 typedef uint32_t bits;392 393 static void get_bits(long double x, uint32_t& a)394 {395 std::memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);396 }397 398 static void set_bits(long double& x, uint32_t a)399 {400 std::memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);401 }402 403private:404 BOOST_MATH_STATIC constexpr int offset_ = BOOST_MATH_ENDIAN_BIG_BYTE ? 0 : 12;405};406 407 408// long double (>64 bits), Motorola 68K ----------------------------------------409 410#elif defined(__m68k) || defined(__m68k__) \411 || defined(__mc68000) || defined(__mc68000__) \412 413// Motorola extended double precision format (96 bits)414 415// It is the same format as the Intel extended double precision format,416// except that 1) it is big-endian, 2) the 3rd and 4th byte are padding, and417// 3) the flag bit is not set for infinity418 419template<>420struct fp_traits_non_native<long double, extended_double_precision>421{422 typedef ieee_copy_leading_bits_tag method;423 424 BOOST_MATH_STATIC constexpr uint32_t sign = 0x80000000u;425 BOOST_MATH_STATIC constexpr uint32_t exponent = 0x7fff0000;426 BOOST_MATH_STATIC constexpr uint32_t flag = 0x00008000;427 BOOST_MATH_STATIC constexpr uint32_t significand = 0x00007fff;428 429 // copy 1st, 2nd, 5th and 6th byte. 3rd and 4th byte are padding.430 431 typedef uint32_t bits;432 433 static void get_bits(long double x, uint32_t& a)434 {435 std::memcpy(&a, &x, 2);436 std::memcpy(reinterpret_cast<unsigned char*>(&a) + 2,437 reinterpret_cast<const unsigned char*>(&x) + 4, 2);438 }439 440 static void set_bits(long double& x, uint32_t a)441 {442 std::memcpy(&x, &a, 2);443 std::memcpy(reinterpret_cast<unsigned char*>(&x) + 4,444 reinterpret_cast<const unsigned char*>(&a) + 2, 2);445 }446};447 448 449// long double (>64 bits), All other processors --------------------------------450 451#else452 453// IEEE extended double precision format with 15 exponent bits (128 bits)454 455template<>456struct fp_traits_non_native<long double, extended_double_precision>457{458 typedef ieee_copy_leading_bits_tag method;459 460 BOOST_MATH_STATIC constexpr uint32_t sign = 0x80000000u;461 BOOST_MATH_STATIC constexpr uint32_t exponent = 0x7fff0000;462 BOOST_MATH_STATIC constexpr uint32_t flag = 0x00000000;463 BOOST_MATH_STATIC constexpr uint32_t significand = 0x0000ffff;464 465 typedef uint32_t bits;466 467 static void get_bits(long double x, uint32_t& a)468 {469 std::memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);470 }471 472 static void set_bits(long double& x, uint32_t a)473 {474 std::memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);475 }476 477private:478 BOOST_MATH_STATIC constexpr int offset_ = BOOST_MATH_ENDIAN_BIG_BYTE ? 0 : 12;479};480 481#endif482 483//------------------------------------------------------------------------------484 485// size_to_precision is a type switch for converting a C++ floating point type486// to the corresponding precision type.487 488template<size_t n, bool fp> struct size_to_precision489{490 typedef unknown_precision type;491};492 493template<> struct size_to_precision<4, true>494{495 typedef single_precision type;496};497 498template<> struct size_to_precision<8, true>499{500 typedef double_precision type;501};502 503template<> struct size_to_precision<10, true>504{505 typedef extended_double_precision type;506};507 508template<> struct size_to_precision<12, true>509{510 typedef extended_double_precision type;511};512 513template<> struct size_to_precision<16, true>514{515 typedef extended_double_precision type;516};517 518//------------------------------------------------------------------------------519//520// Figure out whether to use native classification functions based on521// whether T is a built in floating point type or not:522//523template <class T>524struct select_native525{526 typedef typename size_to_precision<sizeof(T), ::std::is_floating_point<T>::value>::type precision;527 typedef fp_traits_non_native<T, precision> type;528};529template<>530struct select_native<float>531{532 typedef fp_traits_native<float> type;533};534template<>535struct select_native<double>536{537 typedef fp_traits_native<double> type;538};539template<>540struct select_native<long double>541{542 typedef fp_traits_native<long double> type;543};544 545//------------------------------------------------------------------------------546 547// fp_traits is a type switch that selects the right fp_traits_non_native548 549#if (defined(BOOST_MATH_USE_C99) && !(defined(__GNUC__) && (__GNUC__ < 4))) \550 && !defined(__hpux) \551 && !defined(__DECCXX)\552 && !defined(__osf__) \553 && !defined(__SGI_STL_PORT) && !defined(_STLPORT_VERSION)\554 && !defined(__FAST_MATH__)\555 && !defined(BOOST_MATH_DISABLE_STD_FPCLASSIFY)\556 && !defined(__INTEL_COMPILER)\557 && !defined(sun)\558 && !defined(__VXWORKS__)\559 && !defined(BOOST_MATH_HAS_GPU_SUPPORT)560# define BOOST_MATH_USE_STD_FPCLASSIFY561#endif562 563template<class T> struct fp_traits564{565 typedef typename size_to_precision<sizeof(T), ::std::is_floating_point<T>::value>::type precision;566#if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && !defined(BOOST_MATH_DISABLE_STD_FPCLASSIFY)567 typedef typename select_native<T>::type type;568#else569 typedef fp_traits_non_native<T, precision> type;570#endif571 typedef fp_traits_non_native<T, precision> sign_change_type;572};573 574//------------------------------------------------------------------------------575 576} // namespace detail577} // namespace math578} // namespace boost579 580#endif581