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 <math.h>
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>
21/*!
22  \file fpclassify.hpp
23  \brief Classify floating-point value as normal, subnormal, zero, infinite, or NaN.
24  \version 1.0
25  \author John Maddock
26 */
27
28/*
29
301. If the platform is C99 compliant, then the native floating point
31classification functions are used.  However, note that we must only
32define the functions which call std::fpclassify etc if that function
33really does exist: otherwise a compiler may reject the code even though
34the template is never instantiated.
35
362. If the platform is not C99 compliant, and the binary format for
37a floating point type (float, double or long double) can be determined
38at compile time, then the following algorithm is used:
39
40        If all exponent bits, the flag bit (if there is one),
41        and all significand bits are 0, then the number is zero.
42
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.
45
46        If all exponent bits are 1 and all significand bits are 0,
47        then the number is infinity.
48
49        If all exponent bits are 1 and at least one significand bit is 1,
50        then the number is a not-a-number.
51
52        Otherwise the number is normal.
53
54        This algorithm works for the IEEE 754 representation,
55        and also for several non IEEE 754 formats.
56
57    Most formats have the structure
58        sign bit + exponent bits + significand bits.
59
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.
65
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.
72
733. If the platform is not C99 compliant, and the binary format for
74a floating point type (float, double or long double) can not be determined
75at compile time, then comparison with std::numeric_limits values
76is used.
77
78*/
79
80#if defined(_MSC_VER) || defined(__BORLANDC__)
81#include <float.h>
82#endif
83
84#ifdef BOOST_NO_STDC_NAMESPACE
85  namespace std{ using ::abs; using ::fabs; }
86#endif
87
88namespace boost{
89
90//
91// This must not be located in any namespace under boost::math
92// otherwise we can get into an infinite loop if isnan is
93// a #define for "isnan" !
94//
95namespace math_detail{
96
97template <class T>
98inline bool is_nan_helper(T t, const boost::true_type&)
99{
100#ifdef isnan
101   return isnan(t);
102#elif defined(BOOST_MATH_DISABLE_STD_FPCLASSIFY) || !defined(BOOST_HAS_FPCLASSIFY)
103   return false;
104#else // BOOST_HAS_FPCLASSIFY
105   return (BOOST_FPCLASSIFY_PREFIX fpclassify(t) == (int)FP_NAN);
106#endif
107}
108
109template <class T>
110inline bool is_nan_helper(T, const boost::false_type&)
111{
112   return false;
113}
114
115}
116
117namespace math{
118
119namespace detail{
120
121#ifdef BOOST_MATH_USE_STD_FPCLASSIFY
122template <class T>
123inline int fpclassify_imp BOOST_NO_MACRO_EXPAND(T t, const native_tag&)
124{
125   return (std::fpclassify)(t);
126}
127#endif
128
129template <class T>
130inline int fpclassify_imp BOOST_NO_MACRO_EXPAND(T t, const generic_tag<true>&)
131{
132   BOOST_MATH_INSTRUMENT_VARIABLE(t);
133
134   // whenever possible check for Nan's first:
135#if defined(BOOST_HAS_FPCLASSIFY)  && !defined(BOOST_MATH_DISABLE_STD_FPCLASSIFY)
136   if(::boost::math_detail::is_nan_helper(t, ::boost::is_floating_point<T>()))
137      return FP_NAN;
138#elif defined(isnan)
139   if(boost::math_detail::is_nan_helper(t, ::boost::is_floating_point<T>()))
140      return FP_NAN;
141#elif defined(_MSC_VER) || defined(__BORLANDC__)
142   if(::_isnan(boost::math::tools::real_cast<double>(t)))
143      return FP_NAN;
144#endif
145   // std::fabs broken on a few systems especially for long long!!!!
146   T at = (t < T(0)) ? -t : t;
147
148   // Use a process of exclusion to figure out
149   // what kind of type we have, this relies on
150   // IEEE conforming reals that will treat
151   // Nan's as unordered.  Some compilers
152   // don't do this once optimisations are
153   // turned on, hence the check for nan's above.
154   if(at <= (std::numeric_limits<T>::max)())
155   {
156      if(at >= (std::numeric_limits<T>::min)())
157         return FP_NORMAL;
158      return (at != 0) ? FP_SUBNORMAL : FP_ZERO;
159   }
160   else if(at > (std::numeric_limits<T>::max)())
161      return FP_INFINITE;
162   return FP_NAN;
163}
164
165template <class T>
166inline int fpclassify_imp BOOST_NO_MACRO_EXPAND(T t, const generic_tag<false>&)
167{
168#ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
169   if(std::numeric_limits<T>::is_specialized)
170      return fpclassify_imp(t, generic_tag<true>());
171#endif
172   //
173   // An unknown type with no numeric_limits support,
174   // so what are we supposed to do we do here?
175   //
176   BOOST_MATH_INSTRUMENT_VARIABLE(t);
177
178   return t == 0 ? FP_ZERO : FP_NORMAL;
179}
180
181template<class T>
182int fpclassify_imp BOOST_NO_MACRO_EXPAND(T x, ieee_copy_all_bits_tag)
183{
184   typedef BOOST_DEDUCED_TYPENAME fp_traits<T>::type traits;
185
186   BOOST_MATH_INSTRUMENT_VARIABLE(x);
187
188   BOOST_DEDUCED_TYPENAME traits::bits a;
189   traits::get_bits(x,a);
190   BOOST_MATH_INSTRUMENT_VARIABLE(a);
191   a &= traits::exponent | traits::flag | traits::significand;
192   BOOST_MATH_INSTRUMENT_VARIABLE((traits::exponent | traits::flag | traits::significand));
193   BOOST_MATH_INSTRUMENT_VARIABLE(a);
194
195   if(a <= traits::significand) {
196      if(a == 0)
197         return FP_ZERO;
198      else
199         return FP_SUBNORMAL;
200   }
201
202   if(a < traits::exponent) return FP_NORMAL;
203
204   a &= traits::significand;
205   if(a == 0) return FP_INFINITE;
206
207   return FP_NAN;
208}
209
210template<class T>
211int fpclassify_imp BOOST_NO_MACRO_EXPAND(T x, ieee_copy_leading_bits_tag)
212{
213   typedef BOOST_DEDUCED_TYPENAME fp_traits<T>::type traits;
214
215   BOOST_MATH_INSTRUMENT_VARIABLE(x);
216
217   BOOST_DEDUCED_TYPENAME traits::bits a;
218   traits::get_bits(x,a);
219   a &= traits::exponent | traits::flag | traits::significand;
220
221   if(a <= traits::significand) {
222      if(x == 0)
223         return FP_ZERO;
224      else
225         return FP_SUBNORMAL;
226   }
227
228   if(a < traits::exponent) return FP_NORMAL;
229
230   a &= traits::significand;
231   traits::set_bits(x,a);
232   if(x == 0) return FP_INFINITE;
233
234   return FP_NAN;
235}
236
237#if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && defined(BOOST_MATH_NO_NATIVE_LONG_DOUBLE_FP_CLASSIFY)
238template <>
239inline int fpclassify_imp<long double> BOOST_NO_MACRO_EXPAND(long double t, const native_tag&)
240{
241   return boost::math::detail::fpclassify_imp(t, generic_tag<true>());
242}
243#endif
244
245}  // namespace detail
246
247template <class T>
248inline int fpclassify BOOST_NO_MACRO_EXPAND(T t)
249{
250   typedef typename detail::fp_traits<T>::type traits;
251   typedef typename traits::method method;
252   typedef typename tools::promote_args<T>::type value_type;
253#ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
254   if(std::numeric_limits<T>::is_specialized && detail::is_generic_tag_false(static_cast<method*>(0)))
255      return detail::fpclassify_imp(static_cast<value_type>(t), detail::generic_tag<true>());
256   return detail::fpclassify_imp(static_cast<value_type>(t), method());
257#else
258   return detail::fpclassify_imp(static_cast<value_type>(t), method());
259#endif
260}
261
262namespace detail {
263
264#ifdef BOOST_MATH_USE_STD_FPCLASSIFY
265    template<class T>
266    inline bool isfinite_impl(T x, native_tag const&)
267    {
268        return (std::isfinite)(x);
269    }
270#endif
271
272    template<class T>
273    inline bool isfinite_impl(T x, generic_tag<true> const&)
274    {
275        return x >= -(std::numeric_limits<T>::max)()
276            && x <= (std::numeric_limits<T>::max)();
277    }
278
279    template<class T>
280    inline bool isfinite_impl(T x, generic_tag<false> const&)
281    {
282#ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
283      if(std::numeric_limits<T>::is_specialized)
284         return isfinite_impl(x, generic_tag<true>());
285#endif
286       (void)x; // warning supression.
287       return true;
288    }
289
290    template<class T>
291    inline bool isfinite_impl(T x, ieee_tag const&)
292    {
293        typedef BOOST_DEDUCED_TYPENAME detail::fp_traits<T>::type traits;
294        BOOST_DEDUCED_TYPENAME traits::bits a;
295        traits::get_bits(x,a);
296        a &= traits::exponent;
297        return a != traits::exponent;
298    }
299
300#if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && defined(BOOST_MATH_NO_NATIVE_LONG_DOUBLE_FP_CLASSIFY)
301template <>
302inline bool isfinite_impl<long double> BOOST_NO_MACRO_EXPAND(long double t, const native_tag&)
303{
304   return boost::math::detail::isfinite_impl(t, generic_tag<true>());
305}
306#endif
307
308}
309
310template<class T>
311inline bool (isfinite)(T x)
312{ //!< \brief return true if floating-point type t is finite.
313   typedef typename detail::fp_traits<T>::type traits;
314   typedef typename traits::method method;
315   typedef typename boost::is_floating_point<T>::type fp_tag;
316   typedef typename tools::promote_args<T>::type value_type;
317   return detail::isfinite_impl(static_cast<value_type>(x), method());
318}
319
320//------------------------------------------------------------------------------
321
322namespace detail {
323
324#ifdef BOOST_MATH_USE_STD_FPCLASSIFY
325    template<class T>
326    inline bool isnormal_impl(T x, native_tag const&)
327    {
328        return (std::isnormal)(x);
329    }
330#endif
331
332    template<class T>
333    inline bool isnormal_impl(T x, generic_tag<true> const&)
334    {
335        if(x < 0) x = -x;
336        return x >= (std::numeric_limits<T>::min)()
337            && x <= (std::numeric_limits<T>::max)();
338    }
339
340    template<class T>
341    inline bool isnormal_impl(T x, generic_tag<false> const&)
342    {
343#ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
344      if(std::numeric_limits<T>::is_specialized)
345         return isnormal_impl(x, generic_tag<true>());
346#endif
347       return !(x == 0);
348    }
349
350    template<class T>
351    inline bool isnormal_impl(T x, ieee_tag const&)
352    {
353        typedef BOOST_DEDUCED_TYPENAME detail::fp_traits<T>::type traits;
354        BOOST_DEDUCED_TYPENAME traits::bits a;
355        traits::get_bits(x,a);
356        a &= traits::exponent | traits::flag;
357        return (a != 0) && (a < traits::exponent);
358    }
359
360#if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && defined(BOOST_MATH_NO_NATIVE_LONG_DOUBLE_FP_CLASSIFY)
361template <>
362inline bool isnormal_impl<long double> BOOST_NO_MACRO_EXPAND(long double t, const native_tag&)
363{
364   return boost::math::detail::isnormal_impl(t, generic_tag<true>());
365}
366#endif
367
368}
369
370template<class T>
371inline bool (isnormal)(T x)
372{
373   typedef typename detail::fp_traits<T>::type traits;
374   typedef typename traits::method method;
375   typedef typename boost::is_floating_point<T>::type fp_tag;
376   typedef typename tools::promote_args<T>::type value_type;
377   return detail::isnormal_impl(static_cast<value_type>(x), method());
378}
379
380//------------------------------------------------------------------------------
381
382namespace detail {
383
384#ifdef BOOST_MATH_USE_STD_FPCLASSIFY
385    template<class T>
386    inline bool isinf_impl(T x, native_tag const&)
387    {
388        return (std::isinf)(x);
389    }
390#endif
391
392    template<class T>
393    inline bool isinf_impl(T x, generic_tag<true> const&)
394    {
395        (void)x; // in case the compiler thinks that x is unused because std::numeric_limits<T>::has_infinity is false
396        return std::numeric_limits<T>::has_infinity
397            && ( x == std::numeric_limits<T>::infinity()
398                 || x == -std::numeric_limits<T>::infinity());
399    }
400
401    template<class T>
402    inline bool isinf_impl(T x, generic_tag<false> const&)
403    {
404#ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
405      if(std::numeric_limits<T>::is_specialized)
406         return isinf_impl(x, generic_tag<true>());
407#endif
408        (void)x; // warning supression.
409        return false;
410    }
411
412    template<class T>
413    inline bool isinf_impl(T x, ieee_copy_all_bits_tag const&)
414    {
415        typedef BOOST_DEDUCED_TYPENAME fp_traits<T>::type traits;
416
417        BOOST_DEDUCED_TYPENAME traits::bits a;
418        traits::get_bits(x,a);
419        a &= traits::exponent | traits::significand;
420        return a == traits::exponent;
421    }
422
423    template<class T>
424    inline bool isinf_impl(T x, ieee_copy_leading_bits_tag const&)
425    {
426        typedef BOOST_DEDUCED_TYPENAME fp_traits<T>::type traits;
427
428        BOOST_DEDUCED_TYPENAME traits::bits a;
429        traits::get_bits(x,a);
430        a &= traits::exponent | traits::significand;
431        if(a != traits::exponent)
432            return false;
433
434        traits::set_bits(x,0);
435        return x == 0;
436    }
437
438#if defined(BOOST_MATH_USE_STD_FPCLASSIFY) && defined(BOOST_MATH_NO_NATIVE_LONG_DOUBLE_FP_CLASSIFY)
439template <>
440inline bool isinf_impl<long double> BOOST_NO_MACRO_EXPAND(long double t, const native_tag&)
441{
442   return boost::math::detail::isinf_impl(t, generic_tag<true>());
443}
444#endif
445
446}   // namespace detail
447
448template<class T>
449inline bool (isinf)(T x)
450{
451   typedef typename detail::fp_traits<T>::type traits;
452   typedef typename traits::method method;
453   typedef typename boost::is_floating_point<T>::type fp_tag;
454   typedef typename tools::promote_args<T>::type value_type;
455   return detail::isinf_impl(static_cast<value_type>(x), method());
456}
457
458//------------------------------------------------------------------------------
459
460namespace detail {
461
462#ifdef BOOST_MATH_USE_STD_FPCLASSIFY
463    template<class T>
464    inline bool isnan_impl(T x, native_tag const&)
465    {
466        return (std::isnan)(x);
467    }
468#endif
469
470    template<class T>
471    inline bool isnan_impl(T x, generic_tag<true> const&)
472    {
473        return std::numeric_limits<T>::has_infinity
474            ? !(x <= std::numeric_limits<T>::infinity())
475            : x != x;
476    }
477
478    template<class T>
479    inline bool isnan_impl(T x, generic_tag<false> const&)
480    {
481#ifdef BOOST_NO_LIMITS_COMPILE_TIME_CONSTANTS
482      if(std::numeric_limits<T>::is_specialized)
483         return isnan_impl(x, generic_tag<true>());
484#endif
485        (void)x; // warning supression
486        return false;
487    }
488
489    template<class T>
490    inline bool isnan_impl(T x, ieee_copy_all_bits_tag const&)
491    {
492        typedef BOOST_DEDUCED_TYPENAME fp_traits<T>::type traits;
493
494        BOOST_DEDUCED_TYPENAME traits::bits a;
495        traits::get_bits(x,a);
496        a &= traits::exponent | traits::significand;
497        return a > traits::exponent;
498    }
499
500    template<class T>
501    inline bool isnan_impl(T x, ieee_copy_leading_bits_tag const&)
502    {
503        typedef BOOST_DEDUCED_TYPENAME fp_traits<T>::type traits;
504
505        BOOST_DEDUCED_TYPENAME traits::bits a;
506        traits::get_bits(x,a);
507
508        a &= traits::exponent | traits::significand;
509        if(a < traits::exponent)
510            return false;
511
512        a &= traits::significand;
513        traits::set_bits(x,a);
514        return x != 0;
515    }
516
517}   // namespace detail
518
519template<class T> bool (isnan)(T x)
520{ //!< \brief return true if floating-point type t is NaN (Not A Number).
521   typedef typename detail::fp_traits<T>::type traits;
522   typedef typename traits::method method;
523   typedef typename boost::is_floating_point<T>::type fp_tag;
524   return detail::isnan_impl(x, method());
525}
526
527#ifdef isnan
528template <> inline bool isnan BOOST_NO_MACRO_EXPAND<float>(float t){ return ::boost::math_detail::is_nan_helper(t, boost::true_type()); }
529template <> inline bool isnan BOOST_NO_MACRO_EXPAND<double>(double t){ return ::boost::math_detail::is_nan_helper(t, boost::true_type()); }
530template <> inline bool isnan BOOST_NO_MACRO_EXPAND<long double>(long double t){ return ::boost::math_detail::is_nan_helper(t, boost::true_type()); }
531#endif
532
533} // namespace math
534} // namespace boost
535
536#endif // BOOST_MATH_FPCLASSIFY_HPP
537
538