1 // fp_traits.hpp
2 
3 #ifndef BOOST_SPIRIT_MATH_FP_TRAITS_HPP
4 #define BOOST_SPIRIT_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 #if defined(__vms) && defined(__DECCXX) && !__IEEE_FLOAT
13 #   error The VAX floating point mode on VMS is not supported.
14 #endif
15 
16 #if defined(_MSC_VER)
17 #pragma once
18 #endif
19 
20 #include <cstring>
21 
22 #include <boost/assert.hpp>
23 #include <boost/cstdint.hpp>
24 #include <boost/detail/endian.hpp>
25 #include <boost/static_assert.hpp>
26 #include <boost/type_traits/is_floating_point.hpp>
27 
28 //------------------------------------------------------------------------------
29 
30 namespace boost {
31 namespace spirit {
32 namespace math {
33 namespace detail {
34 
35 //------------------------------------------------------------------------------
36 
37 /*
38 Most processors support three different floating point precisions:
39 single precision (32 bits), double precision (64 bits)
40 and extended double precision (>64 bits)
41 
42 Note that the C++ type long double can be implemented
43 both as double precision and extended double precision.
44 */
45 
46 struct single_precision_tag {};
47 struct double_precision_tag {};
48 struct extended_double_precision_tag {};
49 
50 //------------------------------------------------------------------------------
51 
52 /*
53 template<class T, class U> struct fp_traits_impl;
54 
55   This is traits class that describes the binary structure of floating
56   point numbers of C++ type T and precision U
57 
58 Requirements:
59 
60   T = float, double or long double
61   U = single_precision_tag, double_precision_tag
62       or extended_double_precision_tag
63 
64 Typedef members:
65 
66   bits -- the target type when copying the leading bytes of a floating
67       point number. It is a typedef for uint32_t or uint64_t.
68 
69   coverage -- tells us whether all bytes are copied or not.
70       It is a typedef for all_bits or not_all_bits.
71 
72 Static data members:
73 
74   sign, exponent, flag, mantissa -- bit masks that give the meaning of the bits
75       in the leading bytes.
76 
77 Static function members:
78 
79   init() -- initializes the static data members, if needed.
80             (Is a no-op in the specialized versions of the template.)
81 
82   get_bits(), set_bits() -- provide access to the leading bytes.
83 */
84 
85 struct all_bits {};
86 struct not_all_bits {};
87 
88 // Generic version -------------------------------------------------------------
89 
90 // The generic version uses run time initialization to determine the floating
91 // point format. It is capable of handling most formats,
92 // but not the Motorola 68K extended double precision format.
93 
94 // Currently the generic version is used only for extended double precision
95 // on Itanium. In all other cases there are specializations of the template
96 // that use compile time initialization.
97 
98 template<class T> struct uint32_t_coverage
99 {
100     typedef not_all_bits type;
101 };
102 
103 template<> struct uint32_t_coverage<single_precision_tag>
104 {
105     typedef all_bits type;
106 };
107 
108 template<class T, class U> struct fp_traits_impl
109 {
110     typedef uint32_t bits;
111     typedef BOOST_DEDUCED_TYPENAME uint32_t_coverage<U>::type coverage;
112 
113     BOOST_STATIC_CONSTANT(uint32_t, sign = 0x80000000);
114     static uint32_t exponent;
115     static uint32_t flag;
116     static uint32_t mantissa;
117 
initboost::spirit::math::detail::fp_traits_impl118     static void init()
119     {
120         if(is_init_) return;
121         do_init_();
122         is_init_ = true;
123     }
124 
get_bitsboost::spirit::math::detail::fp_traits_impl125     static void get_bits(T x, uint32_t& a)
126     {
127         memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
128     }
129 
set_bitsboost::spirit::math::detail::fp_traits_impl130     static void set_bits(T& x, uint32_t a)
131     {
132         memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
133     }
134 
135 private:
136     static size_t offset_;
137     static bool is_init_;
138     static void do_init_();
139 };
140 
141 //..............................................................................
142 
143 template<class T, class U> uint32_t fp_traits_impl<T,U>::exponent;
144 template<class T, class U> uint32_t fp_traits_impl<T,U>::flag;
145 template<class T, class U> uint32_t fp_traits_impl<T,U>::mantissa;
146 template<class T, class U> size_t   fp_traits_impl<T,U>::offset_;
147 template<class T, class U> bool     fp_traits_impl<T,U>::is_init_;
148 
149 // In a single-threaded program, do_init will be called exactly once.
150 // In a multi-threaded program, do_init may be called simultaneously
151 // by more then one thread. That should not be a problem.
152 
153 //..............................................................................
154 
do_init_()155 template<class T, class U> void fp_traits_impl<T,U>::do_init_()
156 {
157     T x = static_cast<T>(3) / static_cast<T>(4);
158     // sign bit = 0
159     // exponent: first and last bit = 0, all other bits  = 1
160     // flag bit (if present) = 1
161     // mantissa: first bit = 1, all other bits = 0
162 
163     uint32_t a;
164 
165     for(size_t k = 0; k <= sizeof(T) - 4; ++k) {
166 
167         memcpy(&a, reinterpret_cast<unsigned char*>(&x) + k, 4);
168 
169         switch(a) {
170 
171         case 0x3f400000:      // IEEE single precision format
172 
173             offset_  = k;
174             exponent = 0x7f800000;
175             flag     = 0x00000000;
176             mantissa = 0x007fffff;
177             return;
178 
179         case 0x3fe80000:      // IEEE double precision format
180                               // and PowerPC extended double precision format
181             offset_  = k;
182             exponent = 0x7ff00000;
183             flag     = 0x00000000;
184             mantissa = 0x000fffff;
185             return;
186 
187         case 0x3ffe0000:      // Motorola extended double precision format
188 
189             // Must not get here. Must be handled by specialization.
190             // To get accurate cutoff between normals and subnormals
191             // we must use the flag bit that is in the 5th byte.
192             // Otherwise this cutoff will be off by a factor 2.
193             // If we do get here, then we have failed to detect the Motorola
194             // processor at compile time.
195 
196             BOOST_ASSERT(false &&
197                 "Failed to detect the Motorola processor at compile time");
198             return;
199 
200         case 0x3ffe8000:      // IEEE extended double precision format
201                               // with 15 exponent bits
202             offset_  = k;
203             exponent = 0x7fff0000;
204             flag     = 0x00000000;
205             mantissa = 0x0000ffff;
206             return;
207 
208         case 0x3ffec000:      // Intel extended double precision format
209 
210             offset_  = k;
211             exponent = 0x7fff0000;
212             flag     = 0x00008000;
213             mantissa = 0x00007fff;
214             return;
215 
216         default:
217             continue;
218         }
219     }
220 
221     BOOST_ASSERT(false);
222 
223     // Unknown format.
224 }
225 
226 
227 // float (32 bits) -------------------------------------------------------------
228 
229 template<> struct fp_traits_impl<float, single_precision_tag>
230 {
231     typedef uint32_t bits;
232     typedef all_bits coverage;
233 
234     BOOST_STATIC_CONSTANT(uint32_t, sign     = 0x80000000);
235     BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7f800000);
236     BOOST_STATIC_CONSTANT(uint32_t, flag     = 0x00000000);
237     BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x007fffff);
238 
initboost::spirit::math::detail::fp_traits_impl239     static void init() {}
get_bitsboost::spirit::math::detail::fp_traits_impl240     static void get_bits(float x, uint32_t& a) { memcpy(&a, &x, 4); }
set_bitsboost::spirit::math::detail::fp_traits_impl241     static void set_bits(float& x, uint32_t a) { memcpy(&x, &a, 4); }
242 };
243 
244 
245 // double (64 bits) ------------------------------------------------------------
246 
247 #if defined(BOOST_NO_INT64_T) || defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION)
248 
249 template<> struct fp_traits_impl<double, double_precision_tag>
250 {
251     typedef uint32_t bits;
252     typedef not_all_bits coverage;
253 
254     BOOST_STATIC_CONSTANT(uint32_t, sign     = 0x80000000);
255     BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7ff00000);
256     BOOST_STATIC_CONSTANT(uint32_t, flag     = 0);
257     BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x000fffff);
258 
initboost::spirit::math::detail::fp_traits_impl259     static void init() {}
260 
get_bitsboost::spirit::math::detail::fp_traits_impl261     static void get_bits(double x, uint32_t& a)
262     {
263         memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
264     }
265 
set_bitsboost::spirit::math::detail::fp_traits_impl266     static void set_bits(double& x, uint32_t a)
267     {
268         memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
269     }
270 
271 private:
272 
273 #if defined(BOOST_BIG_ENDIAN)
274     BOOST_STATIC_CONSTANT(int, offset_ = 0);
275 #elif defined(BOOST_LITTLE_ENDIAN)
276     BOOST_STATIC_CONSTANT(int, offset_ = 4);
277 #else
278     BOOST_STATIC_ASSERT(false);
279 #endif
280 };
281 
282 //..............................................................................
283 
284 #else
285 
286 template<> struct fp_traits_impl<double, double_precision_tag>
287 {
288     typedef uint64_t bits;
289     typedef all_bits coverage;
290 
291     static const uint64_t sign     = (uint64_t)0x80000000 << 32;
292     static const uint64_t exponent = (uint64_t)0x7ff00000 << 32;
293     static const uint64_t flag     = 0;
294     static const uint64_t mantissa
295         = ((uint64_t)0x000fffff << 32) + (uint64_t)0xffffffff;
296 
initboost::spirit::math::detail::fp_traits_impl297     static void init() {}
get_bitsboost::spirit::math::detail::fp_traits_impl298     static void get_bits(double x, uint64_t& a) { memcpy(&a, &x, 8); }
set_bitsboost::spirit::math::detail::fp_traits_impl299     static void set_bits(double& x, uint64_t a) { memcpy(&x, &a, 8); }
300 };
301 
302 #endif
303 
304 
305 // long double (64 bits) -------------------------------------------------------
306 
307 #if defined(BOOST_NO_INT64_T) || defined(BOOST_NO_INCLASS_MEMBER_INITIALIZATION)
308 
309 template<> struct fp_traits_impl<long double, double_precision_tag>
310 {
311     typedef uint32_t bits;
312     typedef not_all_bits coverage;
313 
314     BOOST_STATIC_CONSTANT(uint32_t, sign     = 0x80000000);
315     BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7ff00000);
316     BOOST_STATIC_CONSTANT(uint32_t, flag     = 0);
317     BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x000fffff);
318 
initboost::spirit::math::detail::fp_traits_impl319     static void init() {}
320 
get_bitsboost::spirit::math::detail::fp_traits_impl321     static void get_bits(long double x, uint32_t& a)
322     {
323         memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
324     }
325 
set_bitsboost::spirit::math::detail::fp_traits_impl326     static void set_bits(long double& x, uint32_t a)
327     {
328         memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
329     }
330 
331 private:
332 
333 #if defined(BOOST_BIG_ENDIAN)
334     BOOST_STATIC_CONSTANT(int, offset_ = 0);
335 #elif defined(BOOST_LITTLE_ENDIAN)
336     BOOST_STATIC_CONSTANT(int, offset_ = 4);
337 #else
338     BOOST_STATIC_ASSERT(false);
339 #endif
340 };
341 
342 //..............................................................................
343 
344 #else
345 
346 template<> struct fp_traits_impl<long double, double_precision_tag>
347 {
348     typedef uint64_t bits;
349     typedef all_bits coverage;
350 
351     static const uint64_t sign     = (uint64_t)0x80000000 << 32;
352     static const uint64_t exponent = (uint64_t)0x7ff00000 << 32;
353     static const uint64_t flag     = 0;
354     static const uint64_t mantissa
355         = ((uint64_t)0x000fffff << 32) + (uint64_t)0xffffffff;
356 
initboost::spirit::math::detail::fp_traits_impl357     static void init() {}
get_bitsboost::spirit::math::detail::fp_traits_impl358     static void get_bits(long double x, uint64_t& a) { memcpy(&a, &x, 8); }
set_bitsboost::spirit::math::detail::fp_traits_impl359     static void set_bits(long double& x, uint64_t a) { memcpy(&x, &a, 8); }
360 };
361 
362 #endif
363 
364 
365 // long double (>64 bits), x86 and x64 -----------------------------------------
366 
367 #if defined(__i386) || defined(__i386__) || defined(_M_IX86) \
368     || defined(__amd64) || defined(__amd64__)  || defined(_M_AMD64) \
369     || defined(__x86_64) || defined(__x86_64__) || defined(_M_X64)
370 
371 // Intel extended double precision format (80 bits)
372 
373 template<> struct fp_traits_impl<long double, extended_double_precision_tag>
374 {
375     typedef uint32_t bits;
376     typedef not_all_bits coverage;
377 
378     BOOST_STATIC_CONSTANT(uint32_t, sign     = 0x80000000);
379     BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7fff0000);
380     BOOST_STATIC_CONSTANT(uint32_t, flag     = 0x00008000);
381     BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x00007fff);
382 
initboost::spirit::math::detail::fp_traits_impl383     static void init() {}
384 
get_bitsboost::spirit::math::detail::fp_traits_impl385     static void get_bits(long double x, uint32_t& a)
386     {
387         memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + 6, 4);
388     }
389 
set_bitsboost::spirit::math::detail::fp_traits_impl390     static void set_bits(long double& x, uint32_t a)
391     {
392         memcpy(reinterpret_cast<unsigned char*>(&x) + 6, &a, 4);
393     }
394 };
395 
396 
397 // long double (>64 bits), Itanium ---------------------------------------------
398 
399 #elif defined(__ia64) || defined(__ia64__) || defined(_M_IA64)
400 
401 // The floating point format is unknown at compile time
402 // No template specialization is provided.
403 // The generic definition is used.
404 
405 // The Itanium supports both
406 // the Intel extended double precision format (80 bits) and
407 // the IEEE extended double precision format with 15 exponent bits (128 bits).
408 
409 
410 // long double (>64 bits), PowerPC ---------------------------------------------
411 
412 #elif defined(__powerpc) || defined(__powerpc__) || defined(__POWERPC__) \
413     || defined(__ppc) || defined(__ppc__) || defined(__PPC__)
414 
415 // PowerPC extended double precision format (128 bits)
416 
417 template<> struct fp_traits_impl<long double, extended_double_precision_tag>
418 {
419     typedef uint32_t bits;
420     typedef not_all_bits coverage;
421 
422     BOOST_STATIC_CONSTANT(uint32_t, sign     = 0x80000000);
423     BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7ff00000);
424     BOOST_STATIC_CONSTANT(uint32_t, flag     = 0x00000000);
425     BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x000fffff);
426 
initboost::spirit::math::detail::fp_traits_impl427     static void init() {}
428 
get_bitsboost::spirit::math::detail::fp_traits_impl429     static void get_bits(long double x, uint32_t& a)
430     {
431         memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
432     }
433 
set_bitsboost::spirit::math::detail::fp_traits_impl434     static void set_bits(long double& x, uint32_t a)
435     {
436         memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
437     }
438 
439 private:
440 
441 #if defined(BOOST_BIG_ENDIAN)
442     BOOST_STATIC_CONSTANT(int, offset_ = 0);
443 #elif defined(BOOST_LITTLE_ENDIAN)
444     BOOST_STATIC_CONSTANT(int, offset_ = 12);
445 #else
446     BOOST_STATIC_ASSERT(false);
447 #endif
448 };
449 
450 
451 // long double (>64 bits), Motorola 68K ----------------------------------------
452 
453 #elif defined(__m68k) || defined(__m68k__) \
454     || defined(__mc68000) || defined(__mc68000__) \
455 
456 // Motorola extended double precision format (96 bits)
457 
458 // It is the same format as the Intel extended double precision format,
459 // except that 1) it is big-endian, 2) the 3rd and 4th byte are padding, and
460 // 3) the flag bit is not set for infinity
461 
462 template<> struct fp_traits_impl<long double, extended_double_precision_tag>
463 {
464     typedef uint32_t bits;
465     typedef not_all_bits coverage;
466 
467     BOOST_STATIC_CONSTANT(uint32_t, sign     = 0x80000000);
468     BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7fff0000);
469     BOOST_STATIC_CONSTANT(uint32_t, flag     = 0x00008000);
470     BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x00007fff);
471 
initboost::spirit::math::detail::fp_traits_impl472     static void init() {}
473 
474     // copy 1st, 2nd, 5th and 6th byte. 3rd and 4th byte are padding.
475 
get_bitsboost::spirit::math::detail::fp_traits_impl476     static void get_bits(long double x, uint32_t& a)
477     {
478         memcpy(&a, &x, 2);
479         memcpy(reinterpret_cast<unsigned char*>(&a) + 2,
480                reinterpret_cast<const unsigned char*>(&x) + 4, 2);
481     }
482 
set_bitsboost::spirit::math::detail::fp_traits_impl483     static void set_bits(long double& x, uint32_t a)
484     {
485         memcpy(&x, &a, 2);
486         memcpy(reinterpret_cast<unsigned char*>(&x) + 4,
487                reinterpret_cast<const unsigned char*>(&a) + 2, 2);
488     }
489 };
490 
491 
492 // long double (>64 bits), All other processors --------------------------------
493 
494 #else
495 
496 // IEEE extended double precision format with 15 exponent bits (128 bits)
497 
498 template<> struct fp_traits_impl<long double, extended_double_precision_tag>
499 {
500     typedef uint32_t bits;
501     typedef not_all_bits coverage;
502 
503     BOOST_STATIC_CONSTANT(uint32_t, sign     = 0x80000000);
504     BOOST_STATIC_CONSTANT(uint32_t, exponent = 0x7fff0000);
505     BOOST_STATIC_CONSTANT(uint32_t, flag     = 0x00000000);
506     BOOST_STATIC_CONSTANT(uint32_t, mantissa = 0x0000ffff);
507 
initboost::spirit::math::detail::fp_traits_impl508     static void init() {}
509 
get_bitsboost::spirit::math::detail::fp_traits_impl510     static void get_bits(long double x, uint32_t& a)
511     {
512         memcpy(&a, reinterpret_cast<const unsigned char*>(&x) + offset_, 4);
513     }
514 
set_bitsboost::spirit::math::detail::fp_traits_impl515     static void set_bits(long double& x, uint32_t a)
516     {
517         memcpy(reinterpret_cast<unsigned char*>(&x) + offset_, &a, 4);
518     }
519 
520 private:
521 
522 #if defined(BOOST_BIG_ENDIAN)
523     BOOST_STATIC_CONSTANT(int, offset_ = 0);
524 #elif defined(BOOST_LITTLE_ENDIAN)
525     BOOST_STATIC_CONSTANT(int, offset_ = 12);
526 #else
527     BOOST_STATIC_ASSERT(false);
528 #endif
529 };
530 
531 #endif
532 
533 
534 //------------------------------------------------------------------------------
535 
536 // size_to_precision is a type switch for converting a C++ floating point type
537 // to the corresponding precision type.
538 
539 template<int n> struct size_to_precision;
540 
541 template<> struct size_to_precision<4>
542 {
543     typedef single_precision_tag type;
544 };
545 
546 template<> struct size_to_precision<8>
547 {
548     typedef double_precision_tag type;
549 };
550 
551 template<> struct size_to_precision<10>
552 {
553     typedef extended_double_precision_tag type;
554 };
555 
556 template<> struct size_to_precision<12>
557 {
558     typedef extended_double_precision_tag type;
559 };
560 
561 template<> struct size_to_precision<16>
562 {
563     typedef extended_double_precision_tag type;
564 };
565 
566 // fp_traits is a type switch that selects the right fp_traits_impl
567 
568 template<class T> struct fp_traits
569 {
570     BOOST_STATIC_ASSERT(boost::is_floating_point<T>::value);
571     typedef BOOST_DEDUCED_TYPENAME size_to_precision<sizeof(T)>::type precision;
572     typedef fp_traits_impl<T, precision> type;
573 };
574 
575 
576 //------------------------------------------------------------------------------
577 
578 }   // namespace detail
579 }   // namespace math
580 }   // namespace spirit
581 }   // namespace boost
582 
583 #endif
584