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)
7 #ifndef BOOST_MATH_FPCLASSIFY_HPP
8 #define BOOST_MATH_FPCLASSIFY_HPP
15 #include <type_traits>
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>
22 \brief Classify floating-point value as normal, subnormal, zero, infinite, or NaN.
29 1. If the platform is C99 compliant, then the native floating point
30 classification functions are used. However, note that we must only
31 define the functions which call std::fpclassify etc if that function
32 really does exist: otherwise a compiler may reject the code even though
33 the template is never instantiated.
35 2. If the platform is not C99 compliant, and the binary format for
36 a floating point type (float, double or long double) can be determined
37 at compile time, then the following algorithm is used:
39 If all exponent bits, the flag bit (if there is one),
40 and all significand bits are 0, then the number is zero.
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.
45 If all exponent bits are 1 and all significand bits are 0,
46 then the number is infinity.
48 If all exponent bits are 1 and at least one significand bit is 1,
49 then the number is a not-a-number.
51 Otherwise the number is normal.
53 This algorithm works for the IEEE 754 representation,
54 and also for several non IEEE 754 formats.
56 Most formats have the structure
57 sign bit + exponent bits + significand bits.
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.
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.
72 3. If the platform is not C99 compliant, and the binary format for
73 a floating point type (float, double or long double) can not be determined
74 at compile time, then comparison with std::numeric_limits values
79 #if defined(_MSC_VER) || defined(BOOST_BORLANDC)
82 #ifdef BOOST_MATH_USE_FLOAT128
84 #if __has_include("quadmath.h")
86 #define BOOST_MATH_HAS_QUADMATH_H
91 #ifdef BOOST_NO_STDC_NAMESPACE
92 namespace std{ using ::abs; using ::fabs; }
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" !
102 namespace math_detail{
105 #pragma warning(push)
106 #pragma warning(disable:4800)
110 inline bool is_nan_helper(T t, const std::true_type&)
114 #elif defined(BOOST_MATH_DISABLE_STD_FPCLASSIFY) || !defined(BOOST_HAS_FPCLASSIFY)
117 #else // BOOST_HAS_FPCLASSIFY
118 return (BOOST_FPCLASSIFY_PREFIX fpclassify(t) == (int)FP_NAN);
127 inline bool is_nan_helper(T, const std::false_type&)
131 #if defined(BOOST_MATH_USE_FLOAT128)
132 #if defined(BOOST_MATH_HAS_QUADMATH_H)
133 inline bool is_nan_helper(__float128 f, const std::true_type&) { return ::isnanq(f); }
134 inline 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
137 inline bool is_nan_helper(__float128 f, const std::true_type&) { return std::isnan(static_cast<double>(f)); }
138 inline bool is_nan_helper(__float128 f, const std::false_type&) { return std::isnan(static_cast<double>(f)); }
140 inline bool is_nan_helper(__float128 f, const std::true_type&) { return boost::math::isnan(static_cast<double>(f)); }
141 inline bool is_nan_helper(__float128 f, const std::false_type&) { return boost::math::isnan(static_cast<double>(f)); }
150 #ifdef BOOST_MATH_USE_STD_FPCLASSIFY
152 inline int fpclassify_imp BOOST_NO_MACRO_EXPAND(T t, const native_tag&)
154 return (std::fpclassify)(t);
159 inline int fpclassify_imp BOOST_NO_MACRO_EXPAND(T t, const generic_tag<true>&)
161 BOOST_MATH_INSTRUMENT_VARIABLE(t);
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()))
168 if(boost::math_detail::is_nan_helper(t, typename std::is_floating_point<T>::type()))
170 #elif defined(_MSC_VER) || defined(BOOST_BORLANDC)
171 if(::_isnan(boost::math::tools::real_cast<double>(t)))
174 // std::fabs broken on a few systems especially for long long!!!!
175 T at = (t < T(0)) ? -t : t;
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)())
185 if(at >= (std::numeric_limits<T>::min)())
187 return (at != 0) ? FP_SUBNORMAL : FP_ZERO;
189 else if(at > (std::numeric_limits<T>::max)())
195 inline int fpclassify_imp BOOST_NO_MACRO_EXPAND(T t, const generic_tag<false>&)
197 #ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
198 if(std::numeric_limits<T>::is_specialized)
199 return fpclassify_imp(t, generic_tag<true>());
202 // An unknown type with no numeric_limits support,
203 // so what are we supposed to do we do here?
205 BOOST_MATH_INSTRUMENT_VARIABLE(t);
207 return t == 0 ? FP_ZERO : FP_NORMAL;
211 int fpclassify_imp BOOST_NO_MACRO_EXPAND(T x, ieee_copy_all_bits_tag)
213 typedef typename fp_traits<T>::type traits;
215 BOOST_MATH_INSTRUMENT_VARIABLE(x);
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);
224 if(a <= traits::significand) {
231 if(a < traits::exponent) return FP_NORMAL;
233 a &= traits::significand;
234 if(a == 0) return FP_INFINITE;
240 int fpclassify_imp BOOST_NO_MACRO_EXPAND(T x, ieee_copy_leading_bits_tag)
242 typedef typename fp_traits<T>::type traits;
244 BOOST_MATH_INSTRUMENT_VARIABLE(x);
246 typename traits::bits a;
247 traits::get_bits(x,a);
248 a &= traits::exponent | traits::flag | traits::significand;
250 if(a <= traits::significand) {
257 if(a < traits::exponent) return FP_NORMAL;
259 a &= traits::significand;
260 traits::set_bits(x,a);
261 if(x == 0) return FP_INFINITE;
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))
267 inline int fpclassify_imp BOOST_NO_MACRO_EXPAND(long double t, const native_tag&)
269 return boost::math::detail::fpclassify_imp(t, generic_tag<true>());
273 } // namespace detail
276 inline int fpclassify BOOST_NO_MACRO_EXPAND(T t)
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*>(0)))
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());
286 return detail::fpclassify_imp(static_cast<value_type>(t), method());
290 #ifdef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
292 inline int fpclassify<long double> BOOST_NO_MACRO_EXPAND(long double t)
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*>(0)))
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());
302 return detail::fpclassify_imp(static_cast<value_type>(t), method());
309 #ifdef BOOST_MATH_USE_STD_FPCLASSIFY
311 inline bool isfinite_impl(T x, native_tag const&)
313 return (std::isfinite)(x);
318 inline bool isfinite_impl(T x, generic_tag<true> const&)
320 return x >= -(std::numeric_limits<T>::max)()
321 && x <= (std::numeric_limits<T>::max)();
325 inline bool isfinite_impl(T x, generic_tag<false> const&)
327 #ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
328 if(std::numeric_limits<T>::is_specialized)
329 return isfinite_impl(x, generic_tag<true>());
331 (void)x; // warning suppression.
336 inline bool isfinite_impl(T x, ieee_tag const&)
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;
345 #if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && defined(BOOST_MATH_NO_NATIVE_LONG_DOUBLE_FP_CLASSIFY)
346 inline bool isfinite_impl BOOST_NO_MACRO_EXPAND(long double t, const native_tag&)
348 return boost::math::detail::isfinite_impl(t, generic_tag<true>());
355 inline 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());
364 #ifdef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
366 inline 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());
376 //------------------------------------------------------------------------------
380 #ifdef BOOST_MATH_USE_STD_FPCLASSIFY
382 inline bool isnormal_impl(T x, native_tag const&)
384 return (std::isnormal)(x);
389 inline bool isnormal_impl(T x, generic_tag<true> const&)
392 return x >= (std::numeric_limits<T>::min)()
393 && x <= (std::numeric_limits<T>::max)();
397 inline bool isnormal_impl(T x, generic_tag<false> const&)
399 #ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
400 if(std::numeric_limits<T>::is_specialized)
401 return isnormal_impl(x, generic_tag<true>());
407 inline bool isnormal_impl(T x, ieee_tag const&)
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);
416 #if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && defined(BOOST_MATH_NO_NATIVE_LONG_DOUBLE_FP_CLASSIFY)
417 inline bool isnormal_impl BOOST_NO_MACRO_EXPAND(long double t, const native_tag&)
419 return boost::math::detail::isnormal_impl(t, generic_tag<true>());
426 inline bool (isnormal)(T x)
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());
435 #ifdef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
437 inline bool (isnormal)(long double x)
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());
447 //------------------------------------------------------------------------------
451 #ifdef BOOST_MATH_USE_STD_FPCLASSIFY
453 inline bool isinf_impl(T x, native_tag const&)
455 return (std::isinf)(x);
460 inline bool isinf_impl(T x, generic_tag<true> const&)
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());
469 inline bool isinf_impl(T x, generic_tag<false> const&)
471 #ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
472 if(std::numeric_limits<T>::is_specialized)
473 return isinf_impl(x, generic_tag<true>());
475 (void)x; // warning suppression.
480 inline bool isinf_impl(T x, ieee_copy_all_bits_tag const&)
482 typedef typename fp_traits<T>::type traits;
484 typename traits::bits a;
485 traits::get_bits(x,a);
486 a &= traits::exponent | traits::significand;
487 return a == traits::exponent;
491 inline bool isinf_impl(T x, ieee_copy_leading_bits_tag const&)
493 typedef typename fp_traits<T>::type traits;
495 typename traits::bits a;
496 traits::get_bits(x,a);
497 a &= traits::exponent | traits::significand;
498 if(a != traits::exponent)
501 traits::set_bits(x,0);
505 #if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && defined(BOOST_MATH_NO_NATIVE_LONG_DOUBLE_FP_CLASSIFY)
506 inline bool isinf_impl BOOST_NO_MACRO_EXPAND(long double t, const native_tag&)
508 return boost::math::detail::isinf_impl(t, generic_tag<true>());
512 } // namespace detail
515 inline bool (isinf)(T x)
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());
524 #ifdef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
526 inline bool (isinf)(long double x)
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());
535 #if defined(BOOST_MATH_USE_FLOAT128) && defined(BOOST_MATH_HAS_QUADMATH_H)
537 inline bool (isinf)(__float128 x)
543 //------------------------------------------------------------------------------
547 #ifdef BOOST_MATH_USE_STD_FPCLASSIFY
549 inline bool isnan_impl(T x, native_tag const&)
551 return (std::isnan)(x);
556 inline bool isnan_impl(T x, generic_tag<true> const&)
558 return std::numeric_limits<T>::has_infinity
559 ? !(x <= std::numeric_limits<T>::infinity())
564 inline bool isnan_impl(T x, generic_tag<false> const&)
566 #ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
567 if(std::numeric_limits<T>::is_specialized)
568 return isnan_impl(x, generic_tag<true>());
570 (void)x; // warning suppression
575 inline bool isnan_impl(T x, ieee_copy_all_bits_tag const&)
577 typedef typename fp_traits<T>::type traits;
579 typename traits::bits a;
580 traits::get_bits(x,a);
581 a &= traits::exponent | traits::significand;
582 return a > traits::exponent;
586 inline bool isnan_impl(T x, ieee_copy_leading_bits_tag const&)
588 typedef typename fp_traits<T>::type traits;
590 typename traits::bits a;
591 traits::get_bits(x,a);
593 a &= traits::exponent | traits::significand;
594 if(a < traits::exponent)
597 a &= traits::significand;
598 traits::set_bits(x,a);
602 } // namespace detail
605 inline 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());
614 template <> inline bool isnan BOOST_NO_MACRO_EXPAND<float>(float t){ return ::boost::math_detail::is_nan_helper(t, std::true_type()); }
615 template <> inline bool isnan BOOST_NO_MACRO_EXPAND<double>(double t){ return ::boost::math_detail::is_nan_helper(t, std::true_type()); }
616 template <> 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)
619 inline 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());
627 #if defined(BOOST_MATH_USE_FLOAT128) && defined(BOOST_MATH_HAS_QUADMATH_H)
629 inline bool (isnan)(__float128 x)
637 #endif // BOOST_MATH_FPCLASSIFY_HPP