// 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 |