boost/spirit/home/support/detail/math/detail/fp_traits.hpp
// 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 #if defined(_MSC_VER) #pragma once #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 && "Failed to detect the Motorola processor at compile time"); 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