Home | History | Annotate | Download | only in detail
      1 // fp_traits.hpp
      2 
      3 #ifndef BOOST_MATH_FP_TRAITS_HPP
      4 #define BOOST_MATH_FP_TRAITS_HPP
      5 
      6 // Copyright (c) 2006 Johan Rade
      7 
      8 // Distributed under the Boost Software License, Version 1.0.
      9 // (See accompanying file LICENSE_1_0.txt
     10 // or copy at http://www.boost.org/LICENSE_1_0.txt)
     11 
     12 /*
     13 To support old compilers, care has been taken to avoid partial template
     14 specialization and meta function forwarding.
     15 With these techniques, the code could be simplified.
     16 */
     17 
     18 #if defined(__vms) && defined(__DECCXX) && !__IEEE_FLOAT
     19 // The VAX floating point formats are used (for float and double)
     20 #   define BOOST_FPCLASSIFY_VAX_FORMAT
     21 #endif
     22 
     23 #include <cstring>
     24 
     25 #include <boost/assert.hpp>
     26 #include <boost/cstdint.hpp>
     27 #include <boost/detail/endian.hpp>
     28 #include <boost/static_assert.hpp>
     29 #include <boost/type_traits/is_floating_point.hpp>
     30 
     31 #ifdef BOOST_NO_STDC_NAMESPACE
     32   namespace std{ using ::memcpy; }
     33 #endif
     34 
     35 #ifndef FP_NORMAL
     36 
     37 #define FP_ZERO        0
     38 #define FP_NORMAL      1
     39 #define FP_INFINITE    2
     40 #define FP_NAN         3
     41 #define FP_SUBNORMAL   4
     42 
     43 #else
     44 
     45 #define BOOST_HAS_FPCLASSIFY
     46 
     47 #ifndef fpclassify
     48 #  if (defined(__GLIBCPP__) || defined(__GLIBCXX__)) \
     49          && defined(_GLIBCXX_USE_C99_MATH) \
     50          && !(defined(_GLIBCXX_USE_C99_FP_MACROS_DYNAMIC) \
     51          && (_GLIBCXX_USE_C99_FP_MACROS_DYNAMIC != 0))
     52 #     ifdef _STLP_VENDOR_CSTD
     53 #        if _STLPORT_VERSION >= 0x520
     54 #           define BOOST_FPCLASSIFY_PREFIX ::__std_alias::
     55 #        else
     56 #           define BOOST_FPCLASSIFY_PREFIX ::_STLP_VENDOR_CSTD::
     57 #        endif
     58 #     else
     59 #        define BOOST_FPCLASSIFY_PREFIX ::std::
     60 #     endif
     61 #  else
     62 #     undef BOOST_HAS_FPCLASSIFY
     63 #     define BOOST_FPCLASSIFY_PREFIX
     64 #  endif
     65 #elif (defined(__HP_aCC) && !defined(__hppa))
     66 // aCC 6 appears to do "#define fpclassify fpclassify" which messes us up a bit!
     67 #  define BOOST_FPCLASSIFY_PREFIX ::
     68 #else
     69 #  define BOOST_FPCLASSIFY_PREFIX
     70 #endif
     71 
     72 #ifdef __MINGW32__
     73 #  undef BOOST_HAS_FPCLASSIFY
     74 #endif
     75 
     76 #endif
     77 
     78 
     79 //------------------------------------------------------------------------------
     80 
     81 namespace boost {
     82 namespace math {
     83 namespace detail {
     84 
     85 //------------------------------------------------------------------------------
     86 
     87 /*
     88 The following classes are used to tag the different methods that are used
     89 for floating point classification
     90 */
     91 
     92 struct native_tag {};
     93 template <bool has_limits>
     94 struct generic_tag {};
     95 struct ieee_tag {};
     96 struct ieee_copy_all_bits_tag : public ieee_tag {};
     97 struct ieee_copy_leading_bits_tag : public ieee_tag {};
     98 
     99 #ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
    100 //
    101 // These helper functions are used only when numeric_limits<>
    102 // members are not compile time constants:
    103 //
    104 inline bool is_generic_tag_false(const generic_tag<false>*)
    105 {
    106    return true;
    107 }
    108 inline bool is_generic_tag_false(const void*)
    109 {
    110    return false;
    111 }
    112 #endif
    113 
    114 //------------------------------------------------------------------------------
    115 
    116 /*
    117 Most processors support three different floating point precisions:
    118 single precision (32 bits), double precision (64 bits)
    119 and extended double precision (80 - 128 bits, depending on the processor)
    120 
    121 Note that the C++ type long double can be implemented
    122 both as double precision and extended double precision.
    123 */
    124 
    125 struct unknown_precision{};
    126 struct single_precision {};
    127 struct double_precision {};
    128 struct extended_double_precision {};
    129 
    130 // native_tag version --------------------------------------------------------------
    131 
    132 template<class T> struct fp_traits_native
    133 {
    134     typedef native_tag method;
    135 };
    136 
    137 // generic_tag version -------------------------------------------------------------
    138 
    139 template<class T, class U> struct fp_traits_non_native
    140 {
    141 #ifndef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
    142    typedef generic_tag<std::numeric_limits<T>::is_specialized> method;
    143 #else
    144    typedef generic_tag<false> method;
    145 #endif
    146 };
    147 
    148 // ieee_tag versions ---------------------------------------------------------------
    149 
    150 /*
    151 These specializations of fp_traits_non_native contain information needed
    152 to "parse" the binary representation of a floating point number.
    153 
    154 Typedef members:
    155 
    156   bits -- the target type when copying the leading bytes of a floating
    157       point number. It is a typedef for uint32_t or uint64_t.
    158 
    159   method -- tells us whether all bytes are copied or not.
    160       It is a typedef for ieee_copy_all_bits_tag or ieee_copy_leading_bits_tag.
    161 
    162 Static data members:
    163 
    164   sign, exponent, flag, significand -- bit masks that give the meaning of the
    165   bits in the leading bytes.
    166 
    167 Static function members:
    168 
    169   get_bits(), set_bits() -- provide access to the leading bytes.
    170 
    171 */
    172 
    173 // ieee_tag version, float (32 bits) -----------------------------------------------
    174 
    175 #ifndef BOOST_FPCLASSIFY_VAX_FORMAT
    176 
    177 template<> struct fp_traits_non_native<float, single_precision>
    178 {
    179     typedef ieee_copy_all_bits_tag method;
    180 
    181     BOOST_STATIC_CONSTANT(uint32_t, sign        = 0x80000000u);
    182     BOOST_STATIC_CONSTANT(uint32_t, exponent    = 0x7f800000);
    183     BOOST_STATIC_CONSTANT(uint32_t, flag        = 0x00000000);
    184     BOOST_STATIC_CONSTANT(uint32_t, significand = 0x007fffff);
    185 
    186     typedef uint32_t bits;
    187     static void get_bits(float x, uint32_t& a) { std::memcpy(&a, &x, 4); }
    188     static void set_bits(float& x, uint32_t a) { std::memcpy(&x, &a, 4); }
    189 };
    190 
    191 // ieee_tag version, double (64 bits) ----------------------------------------------
    192 
    193 #if defined(BOOST_NO_INT64_T) || defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION) \
    194    || defined(__BORLANDC__) || defined(__CODEGEAR__)
    195 
    196 template<> struct fp_traits_non_native<double, double_precision>
    197 {
    198     typedef ieee_copy_leading_bits_tag method;
    199 
    200     BOOST_STATIC_CONSTANT(uint32_t, sign        = 0x80000000u);
    201     BOOST_STATIC_CONSTANT(uint32_t, exponent    = 0x7ff00000);
    202     BOOST_STATIC_CONSTANT(uint32_t, flag        = 0);
    203     BOOST_STATIC_CONSTANT(uint32_t, significand = 0x000fffff);
    204 
    205     typedef uint32_t bits;
    206 
    207     static void get_bits(double x, uint32_t& a)
    208     {
    209         std::memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
    210     }
    211 
    212     static void set_bits(double& x, uint32_t a)
    213     {
    214         std::memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
    215     }
    216 
    217 private:
    218 
    219 #if defined(BOOST_BIG_ENDIAN)
    220     BOOST_STATIC_CONSTANT(int, offset_ = 0);
    221 #elif defined(BOOST_LITTLE_ENDIAN)
    222     BOOST_STATIC_CONSTANT(int, offset_ = 4);
    223 #else
    224     BOOST_STATIC_ASSERT(false);
    225 #endif
    226 };
    227 
    228 //..............................................................................
    229 
    230 #else
    231 
    232 template<> struct fp_traits_non_native<double, double_precision>
    233 {
    234     typedef ieee_copy_all_bits_tag method;
    235 
    236     static const uint64_t sign     = ((uint64_t)0x80000000u) << 32;
    237     static const uint64_t exponent = ((uint64_t)0x7ff00000) << 32;
    238     static const uint64_t flag     = 0;
    239     static const uint64_t significand
    240         = (((uint64_t)0x000fffff) << 32) + ((uint64_t)0xffffffffu);
    241 
    242     typedef uint64_t bits;
    243     static void get_bits(double x, uint64_t& a) { std::memcpy(&a, &x, 8); }
    244     static void set_bits(double& x, uint64_t a) { std::memcpy(&x, &a, 8); }
    245 };
    246 
    247 #endif
    248 
    249 #endif  // #ifndef BOOST_FPCLASSIFY_VAX_FORMAT
    250 
    251 // long double (64 bits) -------------------------------------------------------
    252 
    253 #if defined(BOOST_NO_INT64_T) || defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION)\
    254    || defined(__BORLANDC__) || defined(__CODEGEAR__)
    255 
    256 template<> struct fp_traits_non_native<long double, double_precision>
    257 {
    258     typedef ieee_copy_leading_bits_tag method;
    259 
    260     BOOST_STATIC_CONSTANT(uint32_t, sign        = 0x80000000u);
    261     BOOST_STATIC_CONSTANT(uint32_t, exponent    = 0x7ff00000);
    262     BOOST_STATIC_CONSTANT(uint32_t, flag        = 0);
    263     BOOST_STATIC_CONSTANT(uint32_t, significand = 0x000fffff);
    264 
    265     typedef uint32_t bits;
    266 
    267     static void get_bits(long double x, uint32_t& a)
    268     {
    269         std::memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
    270     }
    271 
    272     static void set_bits(long double& x, uint32_t a)
    273     {
    274         std::memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
    275     }
    276 
    277 private:
    278 
    279 #if defined(BOOST_BIG_ENDIAN)
    280     BOOST_STATIC_CONSTANT(int, offset_ = 0);
    281 #elif defined(BOOST_LITTLE_ENDIAN)
    282     BOOST_STATIC_CONSTANT(int, offset_ = 4);
    283 #else
    284     BOOST_STATIC_ASSERT(false);
    285 #endif
    286 };
    287 
    288 //..............................................................................
    289 
    290 #else
    291 
    292 template<> struct fp_traits_non_native<long double, double_precision>
    293 {
    294     typedef ieee_copy_all_bits_tag method;
    295 
    296     static const uint64_t sign     = (uint64_t)0x80000000u << 32;
    297     static const uint64_t exponent = (uint64_t)0x7ff00000 << 32;
    298     static const uint64_t flag     = 0;
    299     static const uint64_t significand
    300         = ((uint64_t)0x000fffff << 32) + (uint64_t)0xffffffffu;
    301 
    302     typedef uint64_t bits;
    303     static void get_bits(long double x, uint64_t& a) { std::memcpy(&a, &x, 8); }
    304     static void set_bits(long double& x, uint64_t a) { std::memcpy(&x, &a, 8); }
    305 };
    306 
    307 #endif
    308 
    309 
    310 // long double (>64 bits), x86 and x64 -----------------------------------------
    311 
    312 #if defined(__i386) || defined(__i386__) || defined(_M_IX86) \
    313     || defined(__amd64) || defined(__amd64__)  || defined(_M_AMD64) \
    314     || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64)
    315 
    316 // Intel extended double precision format (80 bits)
    317 
    318 template<>
    319 struct fp_traits_non_native<long double, extended_double_precision>
    320 {
    321     typedef ieee_copy_leading_bits_tag method;
    322 
    323     BOOST_STATIC_CONSTANT(uint32_t, sign        = 0x80000000u);
    324     BOOST_STATIC_CONSTANT(uint32_t, exponent    = 0x7fff0000);
    325     BOOST_STATIC_CONSTANT(uint32_t, flag        = 0x00008000);
    326     BOOST_STATIC_CONSTANT(uint32_t, significand = 0x00007fff);
    327 
    328     typedef uint32_t bits;
    329 
    330     static void get_bits(long double x, uint32_t& a)
    331     {
    332         std::memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + 6, 4);
    333     }
    334 
    335     static void set_bits(long double& x, uint32_t a)
    336     {
    337         std::memcpy(reinterpret_cast<unsigned char*>(&x) + 6, &a, 4);
    338     }
    339 };
    340 
    341 
    342 // long double (>64 bits), Itanium ---------------------------------------------
    343 
    344 #elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64)
    345 
    346 // The floating point format is unknown at compile time
    347 // No template specialization is provided.
    348 // The generic_tag definition is used.
    349 
    350 // The Itanium supports both
    351 // the Intel extended double precision format (80 bits) and
    352 // the IEEE extended double precision format with 15 exponent bits (128 bits).
    353 
    354 
    355 // long double (>64 bits), PowerPC ---------------------------------------------
    356 
    357 #elif defined(__powerpc) || defined(__powerpc__) || defined(__POWERPC__) \
    358     || defined(__ppc) || defined(__ppc__) || defined(__PPC__)
    359 
    360 // PowerPC extended double precision format (128 bits)
    361 
    362 template<>
    363 struct fp_traits_non_native<long double, extended_double_precision>
    364 {
    365     typedef ieee_copy_leading_bits_tag method;
    366 
    367     BOOST_STATIC_CONSTANT(uint32_t, sign        = 0x80000000u);
    368     BOOST_STATIC_CONSTANT(uint32_t, exponent    = 0x7ff00000);
    369     BOOST_STATIC_CONSTANT(uint32_t, flag        = 0x00000000);
    370     BOOST_STATIC_CONSTANT(uint32_t, significand = 0x000fffff);
    371 
    372     typedef uint32_t bits;
    373 
    374     static void get_bits(long double x, uint32_t& a)
    375     {
    376         std::memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
    377     }
    378 
    379     static void set_bits(long double& x, uint32_t a)
    380     {
    381         std::memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
    382     }
    383 
    384 private:
    385 
    386 #if defined(BOOST_BIG_ENDIAN)
    387     BOOST_STATIC_CONSTANT(int, offset_ = 0);
    388 #elif defined(BOOST_LITTLE_ENDIAN)
    389     BOOST_STATIC_CONSTANT(int, offset_ = 12);
    390 #else
    391     BOOST_STATIC_ASSERT(false);
    392 #endif
    393 };
    394 
    395 
    396 // long double (>64 bits), Motorola 68K ----------------------------------------
    397 
    398 #elif defined(__m68k) || defined(__m68k__) \
    399     || defined(__mc68000) || defined(__mc68000__) \
    400 
    401 // Motorola extended double precision format (96 bits)
    402 
    403 // It is the same format as the Intel extended double precision format,
    404 // except that 1) it is big-endian, 2) the 3rd and 4th byte are padding, and
    405 // 3) the flag bit is not set for infinity
    406 
    407 template<>
    408 struct fp_traits_non_native<long double, extended_double_precision>
    409 {
    410     typedef ieee_copy_leading_bits_tag method;
    411 
    412     BOOST_STATIC_CONSTANT(uint32_t, sign        = 0x80000000u);
    413     BOOST_STATIC_CONSTANT(uint32_t, exponent    = 0x7fff0000);
    414     BOOST_STATIC_CONSTANT(uint32_t, flag        = 0x00008000);
    415     BOOST_STATIC_CONSTANT(uint32_t, significand = 0x00007fff);
    416 
    417     // copy 1st, 2nd, 5th and 6th byte. 3rd and 4th byte are padding.
    418 
    419     typedef uint32_t bits;
    420 
    421     static void get_bits(long double x, uint32_t& a)
    422     {
    423         std::memcpy(&a, &x, 2);
    424         std::memcpy(reinterpret_cast<unsigned char*>(&a) + 2,
    425                reinterpret_cast<const unsigned char*>(&x) + 4, 2);
    426     }
    427 
    428     static void set_bits(long double& x, uint32_t a)
    429     {
    430         std::memcpy(&x, &a, 2);
    431         std::memcpy(reinterpret_cast<unsigned char*>(&x) + 4,
    432                reinterpret_cast<const unsigned char*>(&a) + 2, 2);
    433     }
    434 };
    435 
    436 
    437 // long double (>64 bits), All other processors --------------------------------
    438 
    439 #else
    440 
    441 // IEEE extended double precision format with 15 exponent bits (128 bits)
    442 
    443 template<>
    444 struct fp_traits_non_native<long double, extended_double_precision>
    445 {
    446     typedef ieee_copy_leading_bits_tag method;
    447 
    448     BOOST_STATIC_CONSTANT(uint32_t, sign        = 0x80000000u);
    449     BOOST_STATIC_CONSTANT(uint32_t, exponent    = 0x7fff0000);
    450     BOOST_STATIC_CONSTANT(uint32_t, flag        = 0x00000000);
    451     BOOST_STATIC_CONSTANT(uint32_t, significand = 0x0000ffff);
    452 
    453     typedef uint32_t bits;
    454 
    455     static void get_bits(long double x, uint32_t& a)
    456     {
    457         std::memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
    458     }
    459 
    460     static void set_bits(long double& x, uint32_t a)
    461     {
    462         std::memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
    463     }
    464 
    465 private:
    466 
    467 #if defined(BOOST_BIG_ENDIAN)
    468     BOOST_STATIC_CONSTANT(int, offset_ = 0);
    469 #elif defined(BOOST_LITTLE_ENDIAN)
    470     BOOST_STATIC_CONSTANT(int, offset_ = 12);
    471 #else
    472     BOOST_STATIC_ASSERT(false);
    473 #endif
    474 };
    475 
    476 #endif
    477 
    478 //------------------------------------------------------------------------------
    479 
    480 // size_to_precision is a type switch for converting a C++ floating point type
    481 // to the corresponding precision type.
    482 
    483 template<int n, bool fp> struct size_to_precision
    484 {
    485    typedef unknown_precision type;
    486 };
    487 
    488 template<> struct size_to_precision<4, true>
    489 {
    490     typedef single_precision type;
    491 };
    492 
    493 template<> struct size_to_precision<8, true>
    494 {
    495     typedef double_precision type;
    496 };
    497 
    498 template<> struct size_to_precision<10, true>
    499 {
    500     typedef extended_double_precision type;
    501 };
    502 
    503 template<> struct size_to_precision<12, true>
    504 {
    505     typedef extended_double_precision type;
    506 };
    507 
    508 template<> struct size_to_precision<16, true>
    509 {
    510     typedef extended_double_precision type;
    511 };
    512 
    513 //------------------------------------------------------------------------------
    514 //
    515 // Figure out whether to use native classification functions based on
    516 // whether T is a built in floating point type or not:
    517 //
    518 template <class T>
    519 struct select_native
    520 {
    521     typedef BOOST_DEDUCED_TYPENAME size_to_precision<sizeof(T), ::boost::is_floating_point<T>::value>::type precision;
    522     typedef fp_traits_non_native<T, precision> type;
    523 };
    524 template<>
    525 struct select_native<float>
    526 {
    527     typedef fp_traits_native<float> type;
    528 };
    529 template<>
    530 struct select_native<double>
    531 {
    532     typedef fp_traits_native<double> type;
    533 };
    534 template<>
    535 struct select_native<long double>
    536 {
    537     typedef fp_traits_native<long double> type;
    538 };
    539 
    540 //------------------------------------------------------------------------------
    541 
    542 // fp_traits is a type switch that selects the right fp_traits_non_native
    543 
    544 #if (defined(BOOST_MATH_USE_C99) && !(defined(__GNUC__) && (__GNUC__ < 4))) \
    545    && !defined(__hpux) \
    546    && !defined(__DECCXX)\
    547    && !defined(__osf__) \
    548    && !defined(__SGI_STL_PORT) && !defined(_STLPORT_VERSION)\
    549    && !defined(BOOST_MATH_DISABLE_STD_FPCLASSIFY)
    550 #  define BOOST_MATH_USE_STD_FPCLASSIFY
    551 #endif
    552 
    553 template<class T> struct fp_traits
    554 {
    555     typedef BOOST_DEDUCED_TYPENAME size_to_precision<sizeof(T), ::boost::is_floating_point<T>::value>::type precision;
    556 #if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && !defined(BOOST_MATH_DISABLE_STD_FPCLASSIFY)
    557     typedef typename select_native<T>::type type;
    558 #else
    559     typedef fp_traits_non_native<T, precision> type;
    560 #endif
    561     typedef fp_traits_non_native<T, precision> sign_change_type;
    562 };
    563 
    564 //------------------------------------------------------------------------------
    565 
    566 }   // namespace detail
    567 }   // namespace math
    568 }   // namespace boost
    569 
    570 #endif
    571