1// Copyright John Maddock 2005-2008.
2// Copyright (c) 2006-2008 Johan Rade
3// Use, modification and distribution are subject to the
4// Boost Software License, Version 1.0. (See accompanying file
5// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
6
7#ifndef BOOST_MATH_FPCLASSIFY_HPP
8#define BOOST_MATH_FPCLASSIFY_HPP
9
10#ifdef _MSC_VER
11#pragma once
12#endif
13
14#include <limits>
15#include <type_traits>
16#include <cmath>
17#include <boost/math/tools/real_cast.hpp>
18#include <boost/math/special_functions/math_fwd.hpp>
19#include <boost/math/special_functions/detail/fp_traits.hpp>
20/*!
21 \file fpclassify.hpp
22 \brief Classify floating-point value as normal, subnormal, zero, infinite, or NaN.
23 \version 1.0
24 \author John Maddock
25 */
26
27/*
28
291. If the platform is C99 compliant, then the native floating point
30classification functions are used. However, note that we must only
31define the functions which call std::fpclassify etc if that function
32really does exist: otherwise a compiler may reject the code even though
33the template is never instantiated.
34
352. If the platform is not C99 compliant, and the binary format for
36a floating point type (float, double or long double) can be determined
37at compile time, then the following algorithm is used:
38
39 If all exponent bits, the flag bit (if there is one),
40 and all significand bits are 0, then the number is zero.
41
42 If all exponent bits and the flag bit (if there is one) are 0,
43 and at least one significand bit is 1, then the number is subnormal.
44
45 If all exponent bits are 1 and all significand bits are 0,
46 then the number is infinity.
47
48 If all exponent bits are 1 and at least one significand bit is 1,
49 then the number is a not-a-number.
50
51 Otherwise the number is normal.
52
53 This algorithm works for the IEEE 754 representation,
54 and also for several non IEEE 754 formats.
55
56 Most formats have the structure
57 sign bit + exponent bits + significand bits.
58
59 A few have the structure
60 sign bit + exponent bits + flag bit + significand bits.
61 The flag bit is 0 for zero and subnormal numbers,
62 and 1 for normal numbers and NaN.
63 It is 0 (Motorola 68K) or 1 (Intel) for infinity.
64
65 To get the bits, the four or eight most significant bytes are copied
66 into an uint32_t or uint64_t and bit masks are applied.
67 This covers all the exponent bits and the flag bit (if there is one),
68 but not always all the significand bits.
69 Some of the functions below have two implementations,
70 depending on whether all the significand bits are copied or not.
71
723. If the platform is not C99 compliant, and the binary format for
73a floating point type (float, double or long double) can not be determined
74at compile time, then comparison with std::numeric_limits values
75is used.
76
77*/
78
79#if defined(_MSC_VER) || defined(BOOST_BORLANDC)
80#include <cfloat>
81#endif
82#ifdef BOOST_MATH_USE_FLOAT128
83#ifdef __has_include
84#if __has_include("quadmath.h")
85#include "quadmath.h"
86#define BOOST_MATH_HAS_QUADMATH_H
87#endif
88#endif
89#endif
90
91#ifdef BOOST_NO_STDC_NAMESPACE
92 namespace std{ using ::abs; using ::fabs; }
93#endif
94
95namespace boost{
96
97//
98// This must not be located in any namespace under boost::math
99// otherwise we can get into an infinite loop if isnan is
100// a #define for "isnan" !
101//
102namespace math_detail{
103
104#ifdef _MSC_VER
105#pragma warning(push)
106#pragma warning(disable:4800)
107#endif
108
109template <class T>
110inline bool is_nan_helper(T t, const std::true_type&)
111{
112#ifdef isnan
113 return isnan(t);
114#elif defined(BOOST_MATH_DISABLE_STD_FPCLASSIFY) || !defined(BOOST_HAS_FPCLASSIFY)
115 (void)t;
116 return false;
117#else // BOOST_HAS_FPCLASSIFY
118 return (BOOST_FPCLASSIFY_PREFIX fpclassify(t) == (int)FP_NAN);
119#endif
120}
121
122#ifdef _MSC_VER
123#pragma warning(pop)
124#endif
125
126template <class T>
127inline bool is_nan_helper(T, const std::false_type&)
128{
129 return false;
130}
131#if defined(BOOST_MATH_USE_FLOAT128)
132#if defined(BOOST_MATH_HAS_QUADMATH_H)
133inline bool is_nan_helper(__float128 f, const std::true_type&) { return ::isnanq(f); }
134inline bool is_nan_helper(__float128 f, const std::false_type&) { return ::isnanq(f); }
135#elif defined(BOOST_GNU_STDLIB) && BOOST_GNU_STDLIB && \
136 _GLIBCXX_USE_C99_MATH && !_GLIBCXX_USE_C99_FP_MACROS_DYNAMIC
137inline bool is_nan_helper(__float128 f, const std::true_type&) { return std::isnan(static_cast<double>(f)); }
138inline bool is_nan_helper(__float128 f, const std::false_type&) { return std::isnan(static_cast<double>(f)); }
139#else
140inline bool is_nan_helper(__float128 f, const std::true_type&) { return boost::math::isnan(static_cast<double>(f)); }
141inline bool is_nan_helper(__float128 f, const std::false_type&) { return boost::math::isnan(static_cast<double>(f)); }
142#endif
143#endif
144}
145
146namespace math{
147
148namespace detail{
149
150#ifdef BOOST_MATH_USE_STD_FPCLASSIFY
151template <class T>
152inline int fpclassify_imp BOOST_NO_MACRO_EXPAND(T t, const native_tag&)
153{
154 return (std::fpclassify)(t);
155}
156#endif
157
158template <class T>
159inline int fpclassify_imp BOOST_NO_MACRO_EXPAND(T t, const generic_tag<true>&)
160{
161 BOOST_MATH_INSTRUMENT_VARIABLE(t);
162
163 // whenever possible check for Nan's first:
164#if defined(BOOST_HAS_FPCLASSIFY) && !defined(BOOST_MATH_DISABLE_STD_FPCLASSIFY)
165 if(::boost::math_detail::is_nan_helper(t, typename std::is_floating_point<T>::type()))
166 return FP_NAN;
167#elif defined(isnan)
168 if(boost::math_detail::is_nan_helper(t, typename std::is_floating_point<T>::type()))
169 return FP_NAN;
170#elif defined(_MSC_VER) || defined(BOOST_BORLANDC)
171 if(::_isnan(boost::math::tools::real_cast<double>(t)))
172 return FP_NAN;
173#endif
174 // std::fabs broken on a few systems especially for long long!!!!
175 T at = (t < T(0)) ? -t : t;
176
177 // Use a process of exclusion to figure out
178 // what kind of type we have, this relies on
179 // IEEE conforming reals that will treat
180 // Nan's as unordered. Some compilers
181 // don't do this once optimisations are
182 // turned on, hence the check for nan's above.
183 if(at <= (std::numeric_limits<T>::max)())
184 {
185 if(at >= (std::numeric_limits<T>::min)())
186 return FP_NORMAL;
187 return (at != 0) ? FP_SUBNORMAL : FP_ZERO;
188 }
189 else if(at > (std::numeric_limits<T>::max)())
190 return FP_INFINITE;
191 return FP_NAN;
192}
193
194template <class T>
195inline int fpclassify_imp BOOST_NO_MACRO_EXPAND(T t, const generic_tag<false>&)
196{
197#ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
198 if(std::numeric_limits<T>::is_specialized)
199 return fpclassify_imp(t, generic_tag<true>());
200#endif
201 //
202 // An unknown type with no numeric_limits support,
203 // so what are we supposed to do we do here?
204 //
205 BOOST_MATH_INSTRUMENT_VARIABLE(t);
206
207 return t == 0 ? FP_ZERO : FP_NORMAL;
208}
209
210template<class T>
211int fpclassify_imp BOOST_NO_MACRO_EXPAND(T x, ieee_copy_all_bits_tag)
212{
213 typedef typename fp_traits<T>::type traits;
214
215 BOOST_MATH_INSTRUMENT_VARIABLE(x);
216
217 typename traits::bits a;
218 traits::get_bits(x,a);
219 BOOST_MATH_INSTRUMENT_VARIABLE(a);
220 a &= traits::exponent | traits::flag | traits::significand;
221 BOOST_MATH_INSTRUMENT_VARIABLE((traits::exponent | traits::flag | traits::significand));
222 BOOST_MATH_INSTRUMENT_VARIABLE(a);
223
224 if(a <= traits::significand) {
225 if(a == 0)
226 return FP_ZERO;
227 else
228 return FP_SUBNORMAL;
229 }
230
231 if(a < traits::exponent) return FP_NORMAL;
232
233 a &= traits::significand;
234 if(a == 0) return FP_INFINITE;
235
236 return FP_NAN;
237}
238
239template<class T>
240int fpclassify_imp BOOST_NO_MACRO_EXPAND(T x, ieee_copy_leading_bits_tag)
241{
242 typedef typename fp_traits<T>::type traits;
243
244 BOOST_MATH_INSTRUMENT_VARIABLE(x);
245
246 typename traits::bits a;
247 traits::get_bits(x,a);
248 a &= traits::exponent | traits::flag | traits::significand;
249
250 if(a <= traits::significand) {
251 if(x == 0)
252 return FP_ZERO;
253 else
254 return FP_SUBNORMAL;
255 }
256
257 if(a < traits::exponent) return FP_NORMAL;
258
259 a &= traits::significand;
260 traits::set_bits(x,a);
261 if(x == 0) return FP_INFINITE;
262
263 return FP_NAN;
264}
265
266#if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && (defined(BOOST_MATH_NO_NATIVE_LONG_DOUBLE_FP_CLASSIFY) || defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS))
267inline int fpclassify_imp BOOST_NO_MACRO_EXPAND(long double t, const native_tag&)
268{
269 return boost::math::detail::fpclassify_imp(t, generic_tag<true>());
270}
271#endif
272
273} // namespace detail
274
275template <class T>
276inline int fpclassify BOOST_NO_MACRO_EXPAND(T t)
277{
278 typedef typename detail::fp_traits<T>::type traits;
279 typedef typename traits::method method;
280 typedef typename tools::promote_args_permissive<T>::type value_type;
281#ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
282 if(std::numeric_limits<T>::is_specialized && detail::is_generic_tag_false(static_cast<method*>(nullptr)))
283 return detail::fpclassify_imp(static_cast<value_type>(t), detail::generic_tag<true>());
284 return detail::fpclassify_imp(static_cast<value_type>(t), method());
285#else
286 return detail::fpclassify_imp(static_cast<value_type>(t), method());
287#endif
288}
289
290#ifdef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
291template <>
292inline int fpclassify<long double> BOOST_NO_MACRO_EXPAND(long double t)
293{
294 typedef detail::fp_traits<long double>::type traits;
295 typedef traits::method method;
296 typedef long double value_type;
297#ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
298 if(std::numeric_limits<long double>::is_specialized && detail::is_generic_tag_false(static_cast<method*>(nullptr)))
299 return detail::fpclassify_imp(static_cast<value_type>(t), detail::generic_tag<true>());
300 return detail::fpclassify_imp(static_cast<value_type>(t), method());
301#else
302 return detail::fpclassify_imp(static_cast<value_type>(t), method());
303#endif
304}
305#endif
306
307namespace detail {
308
309#ifdef BOOST_MATH_USE_STD_FPCLASSIFY
310 template<class T>
311 inline bool isfinite_impl(T x, native_tag const&)
312 {
313 return (std::isfinite)(x);
314 }
315#endif
316
317 template<class T>
318 inline bool isfinite_impl(T x, generic_tag<true> const&)
319 {
320 return x >= -(std::numeric_limits<T>::max)()
321 && x <= (std::numeric_limits<T>::max)();
322 }
323
324 template<class T>
325 inline bool isfinite_impl(T x, generic_tag<false> const&)
326 {
327#ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
328 if(std::numeric_limits<T>::is_specialized)
329 return isfinite_impl(x, generic_tag<true>());
330#endif
331 (void)x; // warning suppression.
332 return true;
333 }
334
335 template<class T>
336 inline bool isfinite_impl(T x, ieee_tag const&)
337 {
338 typedef typename detail::fp_traits<T>::type traits;
339 typename traits::bits a;
340 traits::get_bits(x,a);
341 a &= traits::exponent;
342 return a != traits::exponent;
343 }
344
345#if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && defined(BOOST_MATH_NO_NATIVE_LONG_DOUBLE_FP_CLASSIFY)
346inline bool isfinite_impl BOOST_NO_MACRO_EXPAND(long double t, const native_tag&)
347{
348 return boost::math::detail::isfinite_impl(t, generic_tag<true>());
349}
350#endif
351
352}
353
354template<class T>
355inline bool (isfinite)(T x)
356{ //!< \brief return true if floating-point type t is finite.
357 typedef typename detail::fp_traits<T>::type traits;
358 typedef typename traits::method method;
359 // typedef typename boost::is_floating_point<T>::type fp_tag;
360 typedef typename tools::promote_args_permissive<T>::type value_type;
361 return detail::isfinite_impl(static_cast<value_type>(x), method());
362}
363
364#ifdef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
365template<>
366inline bool (isfinite)(long double x)
367{ //!< \brief return true if floating-point type t is finite.
368 typedef detail::fp_traits<long double>::type traits;
369 typedef traits::method method;
370 //typedef boost::is_floating_point<long double>::type fp_tag;
371 typedef long double value_type;
372 return detail::isfinite_impl(static_cast<value_type>(x), method());
373}
374#endif
375
376//------------------------------------------------------------------------------
377
378namespace detail {
379
380#ifdef BOOST_MATH_USE_STD_FPCLASSIFY
381 template<class T>
382 inline bool isnormal_impl(T x, native_tag const&)
383 {
384 return (std::isnormal)(x);
385 }
386#endif
387
388 template<class T>
389 inline bool isnormal_impl(T x, generic_tag<true> const&)
390 {
391 if(x < 0) x = -x;
392 return x >= (std::numeric_limits<T>::min)()
393 && x <= (std::numeric_limits<T>::max)();
394 }
395
396 template<class T>
397 inline bool isnormal_impl(T x, generic_tag<false> const&)
398 {
399#ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
400 if(std::numeric_limits<T>::is_specialized)
401 return isnormal_impl(x, generic_tag<true>());
402#endif
403 return !(x == 0);
404 }
405
406 template<class T>
407 inline bool isnormal_impl(T x, ieee_tag const&)
408 {
409 typedef typename detail::fp_traits<T>::type traits;
410 typename traits::bits a;
411 traits::get_bits(x,a);
412 a &= traits::exponent | traits::flag;
413 return (a != 0) && (a < traits::exponent);
414 }
415
416#if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && defined(BOOST_MATH_NO_NATIVE_LONG_DOUBLE_FP_CLASSIFY)
417inline bool isnormal_impl BOOST_NO_MACRO_EXPAND(long double t, const native_tag&)
418{
419 return boost::math::detail::isnormal_impl(t, generic_tag<true>());
420}
421#endif
422
423}
424
425template<class T>
426inline bool (isnormal)(T x)
427{
428 typedef typename detail::fp_traits<T>::type traits;
429 typedef typename traits::method method;
430 //typedef typename boost::is_floating_point<T>::type fp_tag;
431 typedef typename tools::promote_args_permissive<T>::type value_type;
432 return detail::isnormal_impl(static_cast<value_type>(x), method());
433}
434
435#ifdef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
436template<>
437inline bool (isnormal)(long double x)
438{
439 typedef detail::fp_traits<long double>::type traits;
440 typedef traits::method method;
441 //typedef boost::is_floating_point<long double>::type fp_tag;
442 typedef long double value_type;
443 return detail::isnormal_impl(static_cast<value_type>(x), method());
444}
445#endif
446
447//------------------------------------------------------------------------------
448
449namespace detail {
450
451#ifdef BOOST_MATH_USE_STD_FPCLASSIFY
452 template<class T>
453 inline bool isinf_impl(T x, native_tag const&)
454 {
455 return (std::isinf)(x);
456 }
457#endif
458
459 template<class T>
460 inline bool isinf_impl(T x, generic_tag<true> const&)
461 {
462 (void)x; // in case the compiler thinks that x is unused because std::numeric_limits<T>::has_infinity is false
463 return std::numeric_limits<T>::has_infinity
464 && ( x == std::numeric_limits<T>::infinity()
465 || x == -std::numeric_limits<T>::infinity());
466 }
467
468 template<class T>
469 inline bool isinf_impl(T x, generic_tag<false> const&)
470 {
471#ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
472 if(std::numeric_limits<T>::is_specialized)
473 return isinf_impl(x, generic_tag<true>());
474#endif
475 (void)x; // warning suppression.
476 return false;
477 }
478
479 template<class T>
480 inline bool isinf_impl(T x, ieee_copy_all_bits_tag const&)
481 {
482 typedef typename fp_traits<T>::type traits;
483
484 typename traits::bits a;
485 traits::get_bits(x,a);
486 a &= traits::exponent | traits::significand;
487 return a == traits::exponent;
488 }
489
490 template<class T>
491 inline bool isinf_impl(T x, ieee_copy_leading_bits_tag const&)
492 {
493 typedef typename fp_traits<T>::type traits;
494
495 typename traits::bits a;
496 traits::get_bits(x,a);
497 a &= traits::exponent | traits::significand;
498 if(a != traits::exponent)
499 return false;
500
501 traits::set_bits(x,0);
502 return x == 0;
503 }
504
505#if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && defined(BOOST_MATH_NO_NATIVE_LONG_DOUBLE_FP_CLASSIFY)
506inline bool isinf_impl BOOST_NO_MACRO_EXPAND(long double t, const native_tag&)
507{
508 return boost::math::detail::isinf_impl(t, generic_tag<true>());
509}
510#endif
511
512} // namespace detail
513
514template<class T>
515inline bool (isinf)(T x)
516{
517 typedef typename detail::fp_traits<T>::type traits;
518 typedef typename traits::method method;
519 // typedef typename boost::is_floating_point<T>::type fp_tag;
520 typedef typename tools::promote_args_permissive<T>::type value_type;
521 return detail::isinf_impl(static_cast<value_type>(x), method());
522}
523
524#ifdef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
525template<>
526inline bool (isinf)(long double x)
527{
528 typedef detail::fp_traits<long double>::type traits;
529 typedef traits::method method;
530 //typedef boost::is_floating_point<long double>::type fp_tag;
531 typedef long double value_type;
532 return detail::isinf_impl(static_cast<value_type>(x), method());
533}
534#endif
535#if defined(BOOST_MATH_USE_FLOAT128) && defined(BOOST_MATH_HAS_QUADMATH_H)
536template<>
537inline bool (isinf)(__float128 x)
538{
539 return ::isinfq(x);
540}
541#endif
542
543//------------------------------------------------------------------------------
544
545namespace detail {
546
547#ifdef BOOST_MATH_USE_STD_FPCLASSIFY
548 template<class T>
549 inline bool isnan_impl(T x, native_tag const&)
550 {
551 return (std::isnan)(x);
552 }
553#endif
554
555 template<class T>
556 inline bool isnan_impl(T x, generic_tag<true> const&)
557 {
558 return std::numeric_limits<T>::has_infinity
559 ? !(x <= std::numeric_limits<T>::infinity())
560 : x != x;
561 }
562
563 template<class T>
564 inline bool isnan_impl(T x, generic_tag<false> const&)
565 {
566#ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
567 if(std::numeric_limits<T>::is_specialized)
568 return isnan_impl(x, generic_tag<true>());
569#endif
570 (void)x; // warning suppression
571 return false;
572 }
573
574 template<class T>
575 inline bool isnan_impl(T x, ieee_copy_all_bits_tag const&)
576 {
577 typedef typename fp_traits<T>::type traits;
578
579 typename traits::bits a;
580 traits::get_bits(x,a);
581 a &= traits::exponent | traits::significand;
582 return a > traits::exponent;
583 }
584
585 template<class T>
586 inline bool isnan_impl(T x, ieee_copy_leading_bits_tag const&)
587 {
588 typedef typename fp_traits<T>::type traits;
589
590 typename traits::bits a;
591 traits::get_bits(x,a);
592
593 a &= traits::exponent | traits::significand;
594 if(a < traits::exponent)
595 return false;
596
597 a &= traits::significand;
598 traits::set_bits(x,a);
599 return x != 0;
600 }
601
602} // namespace detail
603
604template<class T>
605inline bool (isnan)(T x)
606{ //!< \brief return true if floating-point type t is NaN (Not A Number).
607 typedef typename detail::fp_traits<T>::type traits;
608 typedef typename traits::method method;
609 // typedef typename boost::is_floating_point<T>::type fp_tag;
610 return detail::isnan_impl(x, method());
611}
612
613#ifdef isnan
614template <> inline bool isnan BOOST_NO_MACRO_EXPAND<float>(float t){ return ::boost::math_detail::is_nan_helper(t, std::true_type()); }
615template <> inline bool isnan BOOST_NO_MACRO_EXPAND<double>(double t){ return ::boost::math_detail::is_nan_helper(t, std::true_type()); }
616template <> inline bool isnan BOOST_NO_MACRO_EXPAND<long double>(long double t){ return ::boost::math_detail::is_nan_helper(t, std::true_type()); }
617#elif defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS)
618template<>
619inline bool (isnan)(long double x)
620{ //!< \brief return true if floating-point type t is NaN (Not A Number).
621 typedef detail::fp_traits<long double>::type traits;
622 typedef traits::method method;
623 //typedef boost::is_floating_point<long double>::type fp_tag;
624 return detail::isnan_impl(x, method());
625}
626#endif
627#if defined(BOOST_MATH_USE_FLOAT128) && defined(BOOST_MATH_HAS_QUADMATH_H)
628template<>
629inline bool (isnan)(__float128 x)
630{
631 return ::isnanq(x);
632}
633#endif
634
635} // namespace math
636} // namespace boost
637#endif // BOOST_MATH_FPCLASSIFY_HPP
638
639

source code of boost/libs/math/include/boost/math/special_functions/fpclassify.hpp