| // fp_traits.hpp |
| |
| #ifndef BOOST_SPIRIT_MATH_FP_TRAITS_HPP |
| #define BOOST_SPIRIT_MATH_FP_TRAITS_HPP |
| |
| // Copyright (c) 2006 Johan Rade |
| |
| // Distributed under the Boost Software License, Version 1.0. |
| // (See accompanying file LICENSE_1_0.txt |
| // or copy at http://www.boost.org/LICENSE_1_0.txt) |
| |
| #if defined(__vms) && defined(__DECCXX) && !__IEEE_FLOAT |
| # error The VAX floating point mode on VMS is not supported. |
| #endif |
| |
| #include <cstring> |
| |
| #include <boost/assert.hpp> |
| #include <boost/cstdint.hpp> |
| #include <boost/detail/endian.hpp> |
| #include <boost/static_assert.hpp> |
| #include <boost/type_traits/is_floating_point.hpp> |
| |
| //------------------------------------------------------------------------------ |
| |
| namespace boost { |
| namespace spirit { |
| namespace math { |
| namespace detail { |
| |
| //------------------------------------------------------------------------------ |
| |
| /* |
| Most processors support three different floating point precisions: |
| single precision (32 bits), double precision (64 bits) |
| and extended double precision (>64 bits) |
| |
| Note that the C++ type long double can be implemented |
| both as double precision and extended double precision. |
| */ |
| |
| struct single_precision_tag {}; |
| struct double_precision_tag {}; |
| struct extended_double_precision_tag {}; |
| |
| //------------------------------------------------------------------------------ |
| |
| /* |
| template<class T, class U> struct fp_traits_impl; |
| |
| This is traits class that describes the binary structure of floating |
| point numbers of C++ type T and precision U |
| |
| Requirements: |
| |
| T = float, double or long double |
| U = single_precision_tag, double_precision_tag |
| or extended_double_precision_tag |
| |
| Typedef members: |
| |
| bits -- the target type when copying the leading bytes of a floating |
| point number. It is a typedef for uint32_t or uint64_t. |
| |
| coverage -- tells us whether all bytes are copied or not. |
| It is a typedef for all_bits or not_all_bits. |
| |
| Static data members: |
| |
| sign, exponent, flag, mantissa -- bit masks that give the meaning of the bits |
| in the leading bytes. |
| |
| Static function members: |
| |
| init() -- initializes the static data members, if needed. |
| (Is a no-op in the specialized versions of the template.) |
| |
| get_bits(), set_bits() -- provide access to the leading bytes. |
| */ |
| |
| struct all_bits {}; |
| struct not_all_bits {}; |
| |
| // Generic version ------------------------------------------------------------- |
| |
| // The generic version uses run time initialization to determine the floating |
| // point format. It is capable of handling most formats, |
| // but not the Motorola 68K extended double precision format. |
| |
| // Currently the generic version is used only for extended double precision |
| // on Itanium. In all other cases there are specializations of the template |
| // that use compile time initialization. |
| |
| template<class T> struct uint32_t_coverage |
| { |
| typedef not_all_bits type; |
| }; |
| |
| template<> struct uint32_t_coverage<single_precision_tag> |
| { |
| typedef all_bits type; |
| }; |
| |
| template<class T, class U> struct fp_traits_impl |
| { |
| typedef uint32_t bits; |
| typedef BOOST_DEDUCED_TYPENAME uint32_t_coverage<U>::type coverage; |
| |
| BOOST_STATIC_CONSTANT(uint32_t, sign = 0x80000000); |
| static uint32_t exponent; |
| static uint32_t flag; |
| static uint32_t mantissa; |
| |
| static void init() |
| { |
| if(is_init_) return; |
| do_init_(); |
| is_init_ = true; |
| } |
| |
| static void get_bits(T x, uint32_t& a) |
| { |
| memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4); |
| } |
| |
| static void set_bits(T& x, uint32_t a) |
| { |
| memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4); |
| } |
| |
| private: |
| static size_t offset_; |
| static bool is_init_; |
| static void do_init_(); |
| }; |
| |
| //.............................................................................. |
| |
| template<class T, class U> uint32_t fp_traits_impl<T,U>::exponent; |
| template<class T, class U> uint32_t fp_traits_impl<T,U>::flag; |
| template<class T, class U> uint32_t fp_traits_impl<T,U>::mantissa; |
| template<class T, class U> size_t fp_traits_impl<T,U>::offset_; |
| template<class T, class U> bool fp_traits_impl<T,U>::is_init_; |
| |
| // In a single-threaded program, do_init will be called exactly once. |
| // In a multi-threaded program, do_init may be called simultaneously |
| // by more then one thread. That should not be a problem. |
| |
| //.............................................................................. |
| |
| template<class T, class U> void fp_traits_impl<T,U>::do_init_() |
| { |
| T x = static_cast<T>(3) / static_cast<T>(4); |
| // sign bit = 0 |
| // exponent: first and last bit = 0, all other bits = 1 |
| // flag bit (if present) = 1 |
| // mantissa: first bit = 1, all other bits = 0 |
| |
| uint32_t a; |
| |
| for(size_t k = 0; k <= sizeof(T) - 4; ++k) { |
| |
| memcpy(&a, reinterpret_cast<unsigned char*>(&x) + k, 4); |
| |
| switch(a) { |
| |
| case 0x3f400000: // IEEE single precision format |
| |
| offset_ = k; |
| exponent = 0x7f800000; |
| flag = 0x00000000; |
| mantissa = 0x007fffff; |
| return; |
| |
| case 0x3fe80000: // IEEE double precision format |
| // and PowerPC extended double precision format |
| offset_ = k; |
| exponent = 0x7ff00000; |
| flag = 0x00000000; |
| mantissa = 0x000fffff; |
| return; |
| |
| case 0x3ffe0000: // Motorola extended double precision format |
| |
| // Must not get here. Must be handled by specialization. |
| // To get accurate cutoff between normals and subnormals |
| // we must use the flag bit that is in the 5th byte. |
| // Otherwise this cutoff will be off by a factor 2. |
| // If we do get here, then we have failed to detect the Motorola |
| // processor at compile time. |
| |
| BOOST_ASSERT(false); |
| return; |
| |
| case 0x3ffe8000: // IEEE extended double precision format |
| // with 15 exponent bits |
| offset_ = k; |
| exponent = 0x7fff0000; |
| flag = 0x00000000; |
| mantissa = 0x0000ffff; |
| return; |
| |
| case 0x3ffec000: // Intel extended double precision format |
| |
| offset_ = k; |
| exponent = 0x7fff0000; |
| flag = 0x00008000; |
| mantissa = 0x00007fff; |
| return; |
| |
| default: |
| continue; |
| } |
| } |
| |
| BOOST_ASSERT(false); |
| |
| // Unknown format. |
| } |
| |
| |
| // float (32 bits) ------------------------------------------------------------- |
| |
| template<> struct fp_traits_impl<float, single_precision_tag> |
| { |
| typedef uint32_t bits; |
| typedef all_bits coverage; |
| |
| BOOST_STATIC_CONSTANT(uint32_t, sign = 0x80000000); |
| BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7f800000); |
| BOOST_STATIC_CONSTANT(uint32_t, flag = 0x00000000); |
| BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x007fffff); |
| |
| static void init() {} |
| static void get_bits(float x, uint32_t& a) { memcpy(&a, &x, 4); } |
| static void set_bits(float& x, uint32_t a) { memcpy(&x, &a, 4); } |
| }; |
| |
| |
| // double (64 bits) ------------------------------------------------------------ |
| |
| #if defined(BOOST_NO_INT64_T) || defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION) |
| |
| template<> struct fp_traits_impl<double, double_precision_tag> |
| { |
| typedef uint32_t bits; |
| typedef not_all_bits coverage; |
| |
| BOOST_STATIC_CONSTANT(uint32_t, sign = 0x80000000); |
| BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7ff00000); |
| BOOST_STATIC_CONSTANT(uint32_t, flag = 0); |
| BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x000fffff); |
| |
| static void init() {} |
| |
| static void get_bits(double x, uint32_t& a) |
| { |
| memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4); |
| } |
| |
| static void set_bits(double& x, uint32_t a) |
| { |
| memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4); |
| } |
| |
| private: |
| |
| #if defined(BOOST_BIG_ENDIAN) |
| BOOST_STATIC_CONSTANT(int, offset_ = 0); |
| #elif defined(BOOST_LITTLE_ENDIAN) |
| BOOST_STATIC_CONSTANT(int, offset_ = 4); |
| #else |
| BOOST_STATIC_ASSERT(false); |
| #endif |
| }; |
| |
| //.............................................................................. |
| |
| #else |
| |
| template<> struct fp_traits_impl<double, double_precision_tag> |
| { |
| typedef uint64_t bits; |
| typedef all_bits coverage; |
| |
| static const uint64_t sign = (uint64_t)0x80000000 << 32; |
| static const uint64_t exponent = (uint64_t)0x7ff00000 << 32; |
| static const uint64_t flag = 0; |
| static const uint64_t mantissa |
| = ((uint64_t)0x000fffff << 32) + (uint64_t)0xffffffff; |
| |
| static void init() {} |
| static void get_bits(double x, uint64_t& a) { memcpy(&a, &x, 8); } |
| static void set_bits(double& x, uint64_t a) { memcpy(&x, &a, 8); } |
| }; |
| |
| #endif |
| |
| |
| // long double (64 bits) ------------------------------------------------------- |
| |
| #if defined(BOOST_NO_INT64_T) || defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION) |
| |
| template<> struct fp_traits_impl<long double, double_precision_tag> |
| { |
| typedef uint32_t bits; |
| typedef not_all_bits coverage; |
| |
| BOOST_STATIC_CONSTANT(uint32_t, sign = 0x80000000); |
| BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7ff00000); |
| BOOST_STATIC_CONSTANT(uint32_t, flag = 0); |
| BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x000fffff); |
| |
| static void init() {} |
| |
| static void get_bits(long double x, uint32_t& a) |
| { |
| memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4); |
| } |
| |
| static void set_bits(long double& x, uint32_t a) |
| { |
| memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4); |
| } |
| |
| private: |
| |
| #if defined(BOOST_BIG_ENDIAN) |
| BOOST_STATIC_CONSTANT(int, offset_ = 0); |
| #elif defined(BOOST_LITTLE_ENDIAN) |
| BOOST_STATIC_CONSTANT(int, offset_ = 4); |
| #else |
| BOOST_STATIC_ASSERT(false); |
| #endif |
| }; |
| |
| //.............................................................................. |
| |
| #else |
| |
| template<> struct fp_traits_impl<long double, double_precision_tag> |
| { |
| typedef uint64_t bits; |
| typedef all_bits coverage; |
| |
| static const uint64_t sign = (uint64_t)0x80000000 << 32; |
| static const uint64_t exponent = (uint64_t)0x7ff00000 << 32; |
| static const uint64_t flag = 0; |
| static const uint64_t mantissa |
| = ((uint64_t)0x000fffff << 32) + (uint64_t)0xffffffff; |
| |
| static void init() {} |
| static void get_bits(long double x, uint64_t& a) { memcpy(&a, &x, 8); } |
| static void set_bits(long double& x, uint64_t a) { memcpy(&x, &a, 8); } |
| }; |
| |
| #endif |
| |
| |
| // long double (>64 bits), x86 and x64 ----------------------------------------- |
| |
| #if defined(__i386) || defined(__i386__) || defined(_M_IX86) \ |
| || defined(__amd64) || defined(__amd64__) || defined(_M_AMD64) \ |
| || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64) |
| |
| // Intel extended double precision format (80 bits) |
| |
| template<> struct fp_traits_impl<long double, extended_double_precision_tag> |
| { |
| typedef uint32_t bits; |
| typedef not_all_bits coverage; |
| |
| BOOST_STATIC_CONSTANT(uint32_t, sign = 0x80000000); |
| BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7fff0000); |
| BOOST_STATIC_CONSTANT(uint32_t, flag = 0x00008000); |
| BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x00007fff); |
| |
| static void init() {} |
| |
| static void get_bits(long double x, uint32_t& a) |
| { |
| memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + 6, 4); |
| } |
| |
| static void set_bits(long double& x, uint32_t a) |
| { |
| memcpy(reinterpret_cast<unsigned char*>(&x) + 6, &a, 4); |
| } |
| }; |
| |
| |
| // long double (>64 bits), Itanium --------------------------------------------- |
| |
| #elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64) |
| |
| // The floating point format is unknown at compile time |
| // No template specialization is provided. |
| // The generic definition is used. |
| |
| // The Itanium supports both |
| // the Intel extended double precision format (80 bits) and |
| // the IEEE extended double precision format with 15 exponent bits (128 bits). |
| |
| |
| // long double (>64 bits), PowerPC --------------------------------------------- |
| |
| #elif defined(__powerpc) || defined(__powerpc__) || defined(__POWERPC__) \ |
| || defined(__ppc) || defined(__ppc__) || defined(__PPC__) |
| |
| // PowerPC extended double precision format (128 bits) |
| |
| template<> struct fp_traits_impl<long double, extended_double_precision_tag> |
| { |
| typedef uint32_t bits; |
| typedef not_all_bits coverage; |
| |
| BOOST_STATIC_CONSTANT(uint32_t, sign = 0x80000000); |
| BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7ff00000); |
| BOOST_STATIC_CONSTANT(uint32_t, flag = 0x00000000); |
| BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x000fffff); |
| |
| static void init() {} |
| |
| static void get_bits(long double x, uint32_t& a) |
| { |
| memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4); |
| } |
| |
| static void set_bits(long double& x, uint32_t a) |
| { |
| memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4); |
| } |
| |
| private: |
| |
| #if defined(BOOST_BIG_ENDIAN) |
| BOOST_STATIC_CONSTANT(int, offset_ = 0); |
| #elif defined(BOOST_LITTLE_ENDIAN) |
| BOOST_STATIC_CONSTANT(int, offset_ = 12); |
| #else |
| BOOST_STATIC_ASSERT(false); |
| #endif |
| }; |
| |
| |
| // long double (>64 bits), Motorola 68K ---------------------------------------- |
| |
| #elif defined(__m68k) || defined(__m68k__) \ |
| || defined(__mc68000) || defined(__mc68000__) \ |
| |
| // Motorola extended double precision format (96 bits) |
| |
| // It is the same format as the Intel extended double precision format, |
| // except that 1) it is big-endian, 2) the 3rd and 4th byte are padding, and |
| // 3) the flag bit is not set for infinity |
| |
| template<> struct fp_traits_impl<long double, extended_double_precision_tag> |
| { |
| typedef uint32_t bits; |
| typedef not_all_bits coverage; |
| |
| BOOST_STATIC_CONSTANT(uint32_t, sign = 0x80000000); |
| BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7fff0000); |
| BOOST_STATIC_CONSTANT(uint32_t, flag = 0x00008000); |
| BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x00007fff); |
| |
| static void init() {} |
| |
| // copy 1st, 2nd, 5th and 6th byte. 3rd and 4th byte are padding. |
| |
| static void get_bits(long double x, uint32_t& a) |
| { |
| memcpy(&a, &x, 2); |
| memcpy(reinterpret_cast<unsigned char*>(&a) + 2, |
| reinterpret_cast<const unsigned char*>(&x) + 4, 2); |
| } |
| |
| static void set_bits(long double& x, uint32_t a) |
| { |
| memcpy(&x, &a, 2); |
| memcpy(reinterpret_cast<unsigned char*>(&x) + 4, |
| reinterpret_cast<const unsigned char*>(&a) + 2, 2); |
| } |
| }; |
| |
| |
| // long double (>64 bits), All other processors -------------------------------- |
| |
| #else |
| |
| // IEEE extended double precision format with 15 exponent bits (128 bits) |
| |
| template<> struct fp_traits_impl<long double, extended_double_precision_tag> |
| { |
| typedef uint32_t bits; |
| typedef not_all_bits coverage; |
| |
| BOOST_STATIC_CONSTANT(uint32_t, sign = 0x80000000); |
| BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7fff0000); |
| BOOST_STATIC_CONSTANT(uint32_t, flag = 0x00000000); |
| BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x0000ffff); |
| |
| static void init() {} |
| |
| static void get_bits(long double x, uint32_t& a) |
| { |
| memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4); |
| } |
| |
| static void set_bits(long double& x, uint32_t a) |
| { |
| memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4); |
| } |
| |
| private: |
| |
| #if defined(BOOST_BIG_ENDIAN) |
| BOOST_STATIC_CONSTANT(int, offset_ = 0); |
| #elif defined(BOOST_LITTLE_ENDIAN) |
| BOOST_STATIC_CONSTANT(int, offset_ = 12); |
| #else |
| BOOST_STATIC_ASSERT(false); |
| #endif |
| }; |
| |
| #endif |
| |
| |
| //------------------------------------------------------------------------------ |
| |
| // size_to_precision is a type switch for converting a C++ floating point type |
| // to the corresponding precision type. |
| |
| template<int n> struct size_to_precision; |
| |
| template<> struct size_to_precision<4> |
| { |
| typedef single_precision_tag type; |
| }; |
| |
| template<> struct size_to_precision<8> |
| { |
| typedef double_precision_tag type; |
| }; |
| |
| template<> struct size_to_precision<10> |
| { |
| typedef extended_double_precision_tag type; |
| }; |
| |
| template<> struct size_to_precision<12> |
| { |
| typedef extended_double_precision_tag type; |
| }; |
| |
| template<> struct size_to_precision<16> |
| { |
| typedef extended_double_precision_tag type; |
| }; |
| |
| // fp_traits is a type switch that selects the right fp_traits_impl |
| |
| template<class T> struct fp_traits |
| { |
| BOOST_STATIC_ASSERT(boost::is_floating_point<T>::value); |
| typedef BOOST_DEDUCED_TYPENAME size_to_precision<sizeof(T)>::type precision; |
| typedef fp_traits_impl<T, precision> type; |
| }; |
| |
| |
| //------------------------------------------------------------------------------ |
| |
| } // namespace detail |
| } // namespace math |
| } // namespace spirit |
| } // namespace boost |
| |
| #endif |