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 <boost/config/no_tr1/cmath.hpp>
16 #include <boost/limits.hpp>
17 #include <boost/math/tools/real_cast.hpp>
18 #include <boost/type_traits/is_floating_point.hpp>
19 #include <boost/math/special_functions/math_fwd.hpp>
20 #include <boost/math/special_functions/detail/fp_traits.hpp>
23 \brief Classify floating-point value as normal, subnormal, zero, infinite, or NaN.
30 1. If the platform is C99 compliant, then the native floating point
31 classification functions are used. However, note that we must only
32 define the functions which call std::fpclassify etc if that function
33 really does exist: otherwise a compiler may reject the code even though
34 the template is never instantiated.
36 2. If the platform is not C99 compliant, and the binary format for
37 a floating point type (float, double or long double) can be determined
38 at compile time, then the following algorithm is used:
40 If all exponent bits, the flag bit (if there is one),
41 and all significand bits are 0, then the number is zero.
43 If all exponent bits and the flag bit (if there is one) are 0,
44 and at least one significand bit is 1, then the number is subnormal.
46 If all exponent bits are 1 and all significand bits are 0,
47 then the number is infinity.
49 If all exponent bits are 1 and at least one significand bit is 1,
50 then the number is a not-a-number.
52 Otherwise the number is normal.
54 This algorithm works for the IEEE 754 representation,
55 and also for several non IEEE 754 formats.
57 Most formats have the structure
58 sign bit + exponent bits + significand bits.
60 A few have the structure
61 sign bit + exponent bits + flag bit + significand bits.
62 The flag bit is 0 for zero and subnormal numbers,
63 and 1 for normal numbers and NaN.
64 It is 0 (Motorola 68K) or 1 (Intel) for infinity.
66 To get the bits, the four or eight most significant bytes are copied
67 into an uint32_t or uint64_t and bit masks are applied.
68 This covers all the exponent bits and the flag bit (if there is one),
69 but not always all the significand bits.
70 Some of the functions below have two implementations,
71 depending on whether all the significand bits are copied or not.
73 3. If the platform is not C99 compliant, and the binary format for
74 a floating point type (float, double or long double) can not be determined
75 at compile time, then comparison with std::numeric_limits values
80 #if defined(_MSC_VER) || defined(BOOST_BORLANDC)
83 #ifdef BOOST_MATH_USE_FLOAT128
85 #if __has_include("quadmath.h")
87 #define BOOST_MATH_HAS_QUADMATH_H
92 #ifdef BOOST_NO_STDC_NAMESPACE
93 namespace std{ using ::abs; using ::fabs; }
99 // This must not be located in any namespace under boost::math
100 // otherwise we can get into an infinite loop if isnan is
101 // a #define for "isnan" !
103 namespace math_detail{
106 #pragma warning(push)
107 #pragma warning(disable:4800)
111 inline bool is_nan_helper(T t, const boost::true_type&)
115 #elif defined(BOOST_MATH_DISABLE_STD_FPCLASSIFY) || !defined(BOOST_HAS_FPCLASSIFY)
118 #else // BOOST_HAS_FPCLASSIFY
119 return (BOOST_FPCLASSIFY_PREFIX fpclassify(t) == (int)FP_NAN);
128 inline bool is_nan_helper(T, const boost::false_type&)
132 #if defined(BOOST_MATH_USE_FLOAT128)
133 #if defined(BOOST_MATH_HAS_QUADMATH_H)
134 inline bool is_nan_helper(__float128 f, const boost::true_type&) { return ::isnanq(f); }
135 inline bool is_nan_helper(__float128 f, const boost::false_type&) { return ::isnanq(f); }
136 #elif defined(BOOST_GNU_STDLIB) && BOOST_GNU_STDLIB && \
137 _GLIBCXX_USE_C99_MATH && !_GLIBCXX_USE_C99_FP_MACROS_DYNAMIC
138 inline bool is_nan_helper(__float128 f, const boost::true_type&) { return std::isnan(static_cast<double>(f)); }
139 inline bool is_nan_helper(__float128 f, const boost::false_type&) { return std::isnan(static_cast<double>(f)); }
141 inline bool is_nan_helper(__float128 f, const boost::true_type&) { return ::isnan(static_cast<double>(f)); }
142 inline bool is_nan_helper(__float128 f, const boost::false_type&) { return ::isnan(static_cast<double>(f)); }
151 #ifdef BOOST_MATH_USE_STD_FPCLASSIFY
153 inline int fpclassify_imp BOOST_NO_MACRO_EXPAND(T t, const native_tag&)
155 return (std::fpclassify)(t);
160 inline int fpclassify_imp BOOST_NO_MACRO_EXPAND(T t, const generic_tag<true>&)
162 BOOST_MATH_INSTRUMENT_VARIABLE(t);
164 // whenever possible check for Nan's first:
165 #if defined(BOOST_HAS_FPCLASSIFY) && !defined(BOOST_MATH_DISABLE_STD_FPCLASSIFY)
166 if(::boost::math_detail::is_nan_helper(t, ::boost::is_floating_point<T>()))
169 if(boost::math_detail::is_nan_helper(t, ::boost::is_floating_point<T>()))
171 #elif defined(_MSC_VER) || defined(BOOST_BORLANDC)
172 if(::_isnan(boost::math::tools::real_cast<double>(t)))
175 // std::fabs broken on a few systems especially for long long!!!!
176 T at = (t < T(0)) ? -t : t;
178 // Use a process of exclusion to figure out
179 // what kind of type we have, this relies on
180 // IEEE conforming reals that will treat
181 // Nan's as unordered. Some compilers
182 // don't do this once optimisations are
183 // turned on, hence the check for nan's above.
184 if(at <= (std::numeric_limits<T>::max)())
186 if(at >= (std::numeric_limits<T>::min)())
188 return (at != 0) ? FP_SUBNORMAL : FP_ZERO;
190 else if(at > (std::numeric_limits<T>::max)())
196 inline int fpclassify_imp BOOST_NO_MACRO_EXPAND(T t, const generic_tag<false>&)
198 #ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
199 if(std::numeric_limits<T>::is_specialized)
200 return fpclassify_imp(t, generic_tag<true>());
203 // An unknown type with no numeric_limits support,
204 // so what are we supposed to do we do here?
206 BOOST_MATH_INSTRUMENT_VARIABLE(t);
208 return t == 0 ? FP_ZERO : FP_NORMAL;
212 int fpclassify_imp BOOST_NO_MACRO_EXPAND(T x, ieee_copy_all_bits_tag)
214 typedef BOOST_DEDUCED_TYPENAME fp_traits<T>::type traits;
216 BOOST_MATH_INSTRUMENT_VARIABLE(x);
218 BOOST_DEDUCED_TYPENAME traits::bits a;
219 traits::get_bits(x,a);
220 BOOST_MATH_INSTRUMENT_VARIABLE(a);
221 a &= traits::exponent | traits::flag | traits::significand;
222 BOOST_MATH_INSTRUMENT_VARIABLE((traits::exponent | traits::flag | traits::significand));
223 BOOST_MATH_INSTRUMENT_VARIABLE(a);
225 if(a <= traits::significand) {
232 if(a < traits::exponent) return FP_NORMAL;
234 a &= traits::significand;
235 if(a == 0) return FP_INFINITE;
241 int fpclassify_imp BOOST_NO_MACRO_EXPAND(T x, ieee_copy_leading_bits_tag)
243 typedef BOOST_DEDUCED_TYPENAME fp_traits<T>::type traits;
245 BOOST_MATH_INSTRUMENT_VARIABLE(x);
247 BOOST_DEDUCED_TYPENAME traits::bits a;
248 traits::get_bits(x,a);
249 a &= traits::exponent | traits::flag | traits::significand;
251 if(a <= traits::significand) {
258 if(a < traits::exponent) return FP_NORMAL;
260 a &= traits::significand;
261 traits::set_bits(x,a);
262 if(x == 0) return FP_INFINITE;
267 #if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && (defined(BOOST_MATH_NO_NATIVE_LONG_DOUBLE_FP_CLASSIFY) || defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS))
268 inline int fpclassify_imp BOOST_NO_MACRO_EXPAND(long double t, const native_tag&)
270 return boost::math::detail::fpclassify_imp(t, generic_tag<true>());
274 } // namespace detail
277 inline int fpclassify BOOST_NO_MACRO_EXPAND(T t)
279 typedef typename detail::fp_traits<T>::type traits;
280 typedef typename traits::method method;
281 typedef typename tools::promote_args_permissive<T>::type value_type;
282 #ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
283 if(std::numeric_limits<T>::is_specialized && detail::is_generic_tag_false(static_cast<method*>(0)))
284 return detail::fpclassify_imp(static_cast<value_type>(t), detail::generic_tag<true>());
285 return detail::fpclassify_imp(static_cast<value_type>(t), method());
287 return detail::fpclassify_imp(static_cast<value_type>(t), method());
291 #ifdef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
293 inline int fpclassify<long double> BOOST_NO_MACRO_EXPAND(long double t)
295 typedef detail::fp_traits<long double>::type traits;
296 typedef traits::method method;
297 typedef long double value_type;
298 #ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
299 if(std::numeric_limits<long double>::is_specialized && detail::is_generic_tag_false(static_cast<method*>(0)))
300 return detail::fpclassify_imp(static_cast<value_type>(t), detail::generic_tag<true>());
301 return detail::fpclassify_imp(static_cast<value_type>(t), method());
303 return detail::fpclassify_imp(static_cast<value_type>(t), method());
310 #ifdef BOOST_MATH_USE_STD_FPCLASSIFY
312 inline bool isfinite_impl(T x, native_tag const&)
314 return (std::isfinite)(x);
319 inline bool isfinite_impl(T x, generic_tag<true> const&)
321 return x >= -(std::numeric_limits<T>::max)()
322 && x <= (std::numeric_limits<T>::max)();
326 inline bool isfinite_impl(T x, generic_tag<false> const&)
328 #ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
329 if(std::numeric_limits<T>::is_specialized)
330 return isfinite_impl(x, generic_tag<true>());
332 (void)x; // warning suppression.
337 inline bool isfinite_impl(T x, ieee_tag const&)
339 typedef BOOST_DEDUCED_TYPENAME detail::fp_traits<T>::type traits;
340 BOOST_DEDUCED_TYPENAME traits::bits a;
341 traits::get_bits(x,a);
342 a &= traits::exponent;
343 return a != traits::exponent;
346 #if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && defined(BOOST_MATH_NO_NATIVE_LONG_DOUBLE_FP_CLASSIFY)
347 inline bool isfinite_impl BOOST_NO_MACRO_EXPAND(long double t, const native_tag&)
349 return boost::math::detail::isfinite_impl(t, generic_tag<true>());
356 inline bool (isfinite)(T x)
357 { //!< \brief return true if floating-point type t is finite.
358 typedef typename detail::fp_traits<T>::type traits;
359 typedef typename traits::method method;
360 // typedef typename boost::is_floating_point<T>::type fp_tag;
361 typedef typename tools::promote_args_permissive<T>::type value_type;
362 return detail::isfinite_impl(static_cast<value_type>(x), method());
365 #ifdef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
367 inline bool (isfinite)(long double x)
368 { //!< \brief return true if floating-point type t is finite.
369 typedef detail::fp_traits<long double>::type traits;
370 typedef traits::method method;
371 //typedef boost::is_floating_point<long double>::type fp_tag;
372 typedef long double value_type;
373 return detail::isfinite_impl(static_cast<value_type>(x), method());
377 //------------------------------------------------------------------------------
381 #ifdef BOOST_MATH_USE_STD_FPCLASSIFY
383 inline bool isnormal_impl(T x, native_tag const&)
385 return (std::isnormal)(x);
390 inline bool isnormal_impl(T x, generic_tag<true> const&)
393 return x >= (std::numeric_limits<T>::min)()
394 && x <= (std::numeric_limits<T>::max)();
398 inline bool isnormal_impl(T x, generic_tag<false> const&)
400 #ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
401 if(std::numeric_limits<T>::is_specialized)
402 return isnormal_impl(x, generic_tag<true>());
408 inline bool isnormal_impl(T x, ieee_tag const&)
410 typedef BOOST_DEDUCED_TYPENAME detail::fp_traits<T>::type traits;
411 BOOST_DEDUCED_TYPENAME traits::bits a;
412 traits::get_bits(x,a);
413 a &= traits::exponent | traits::flag;
414 return (a != 0) && (a < traits::exponent);
417 #if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && defined(BOOST_MATH_NO_NATIVE_LONG_DOUBLE_FP_CLASSIFY)
418 inline bool isnormal_impl BOOST_NO_MACRO_EXPAND(long double t, const native_tag&)
420 return boost::math::detail::isnormal_impl(t, generic_tag<true>());
427 inline bool (isnormal)(T x)
429 typedef typename detail::fp_traits<T>::type traits;
430 typedef typename traits::method method;
431 //typedef typename boost::is_floating_point<T>::type fp_tag;
432 typedef typename tools::promote_args_permissive<T>::type value_type;
433 return detail::isnormal_impl(static_cast<value_type>(x), method());
436 #ifdef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
438 inline bool (isnormal)(long double x)
440 typedef detail::fp_traits<long double>::type traits;
441 typedef traits::method method;
442 //typedef boost::is_floating_point<long double>::type fp_tag;
443 typedef long double value_type;
444 return detail::isnormal_impl(static_cast<value_type>(x), method());
448 //------------------------------------------------------------------------------
452 #ifdef BOOST_MATH_USE_STD_FPCLASSIFY
454 inline bool isinf_impl(T x, native_tag const&)
456 return (std::isinf)(x);
461 inline bool isinf_impl(T x, generic_tag<true> const&)
463 (void)x; // in case the compiler thinks that x is unused because std::numeric_limits<T>::has_infinity is false
464 return std::numeric_limits<T>::has_infinity
465 && ( x == std::numeric_limits<T>::infinity()
466 || x == -std::numeric_limits<T>::infinity());
470 inline bool isinf_impl(T x, generic_tag<false> const&)
472 #ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
473 if(std::numeric_limits<T>::is_specialized)
474 return isinf_impl(x, generic_tag<true>());
476 (void)x; // warning suppression.
481 inline bool isinf_impl(T x, ieee_copy_all_bits_tag const&)
483 typedef BOOST_DEDUCED_TYPENAME fp_traits<T>::type traits;
485 BOOST_DEDUCED_TYPENAME traits::bits a;
486 traits::get_bits(x,a);
487 a &= traits::exponent | traits::significand;
488 return a == traits::exponent;
492 inline bool isinf_impl(T x, ieee_copy_leading_bits_tag const&)
494 typedef BOOST_DEDUCED_TYPENAME fp_traits<T>::type traits;
496 BOOST_DEDUCED_TYPENAME traits::bits a;
497 traits::get_bits(x,a);
498 a &= traits::exponent | traits::significand;
499 if(a != traits::exponent)
502 traits::set_bits(x,0);
506 #if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && defined(BOOST_MATH_NO_NATIVE_LONG_DOUBLE_FP_CLASSIFY)
507 inline bool isinf_impl BOOST_NO_MACRO_EXPAND(long double t, const native_tag&)
509 return boost::math::detail::isinf_impl(t, generic_tag<true>());
513 } // namespace detail
516 inline bool (isinf)(T x)
518 typedef typename detail::fp_traits<T>::type traits;
519 typedef typename traits::method method;
520 // typedef typename boost::is_floating_point<T>::type fp_tag;
521 typedef typename tools::promote_args_permissive<T>::type value_type;
522 return detail::isinf_impl(static_cast<value_type>(x), method());
525 #ifdef BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS
527 inline bool (isinf)(long double x)
529 typedef detail::fp_traits<long double>::type traits;
530 typedef traits::method method;
531 //typedef boost::is_floating_point<long double>::type fp_tag;
532 typedef long double value_type;
533 return detail::isinf_impl(static_cast<value_type>(x), method());
536 #if defined(BOOST_MATH_USE_FLOAT128) && defined(BOOST_MATH_HAS_QUADMATH_H)
538 inline bool (isinf)(__float128 x)
544 //------------------------------------------------------------------------------
548 #ifdef BOOST_MATH_USE_STD_FPCLASSIFY
550 inline bool isnan_impl(T x, native_tag const&)
552 return (std::isnan)(x);
557 inline bool isnan_impl(T x, generic_tag<true> const&)
559 return std::numeric_limits<T>::has_infinity
560 ? !(x <= std::numeric_limits<T>::infinity())
565 inline bool isnan_impl(T x, generic_tag<false> const&)
567 #ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
568 if(std::numeric_limits<T>::is_specialized)
569 return isnan_impl(x, generic_tag<true>());
571 (void)x; // warning suppression
576 inline bool isnan_impl(T x, ieee_copy_all_bits_tag const&)
578 typedef BOOST_DEDUCED_TYPENAME fp_traits<T>::type traits;
580 BOOST_DEDUCED_TYPENAME traits::bits a;
581 traits::get_bits(x,a);
582 a &= traits::exponent | traits::significand;
583 return a > traits::exponent;
587 inline bool isnan_impl(T x, ieee_copy_leading_bits_tag const&)
589 typedef BOOST_DEDUCED_TYPENAME fp_traits<T>::type traits;
591 BOOST_DEDUCED_TYPENAME traits::bits a;
592 traits::get_bits(x,a);
594 a &= traits::exponent | traits::significand;
595 if(a < traits::exponent)
598 a &= traits::significand;
599 traits::set_bits(x,a);
603 } // namespace detail
606 inline bool (isnan)(T x)
607 { //!< \brief return true if floating-point type t is NaN (Not A Number).
608 typedef typename detail::fp_traits<T>::type traits;
609 typedef typename traits::method method;
610 // typedef typename boost::is_floating_point<T>::type fp_tag;
611 return detail::isnan_impl(x, method());
615 template <> inline bool isnan BOOST_NO_MACRO_EXPAND<float>(float t){ return ::boost::math_detail::is_nan_helper(t, boost::true_type()); }
616 template <> inline bool isnan BOOST_NO_MACRO_EXPAND<double>(double t){ return ::boost::math_detail::is_nan_helper(t, boost::true_type()); }
617 template <> inline bool isnan BOOST_NO_MACRO_EXPAND<long double>(long double t){ return ::boost::math_detail::is_nan_helper(t, boost::true_type()); }
618 #elif defined(BOOST_MATH_NO_LONG_DOUBLE_MATH_FUNCTIONS)
620 inline bool (isnan)(long double x)
621 { //!< \brief return true if floating-point type t is NaN (Not A Number).
622 typedef detail::fp_traits<long double>::type traits;
623 typedef traits::method method;
624 //typedef boost::is_floating_point<long double>::type fp_tag;
625 return detail::isnan_impl(x, method());
628 #if defined(BOOST_MATH_USE_FLOAT128) && defined(BOOST_MATH_HAS_QUADMATH_H)
630 inline bool (isnan)(__float128 x)
639 #endif // BOOST_MATH_FPCLASSIFY_HPP