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

source code of include/boost/math/special_functions/detail/fp_traits.hpp