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
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.
35
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:
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
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
76 is 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
88 namespace 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 //
95 namespace math_detail{
96
97 template <class T>
is_nan_helper(T t,const boost::true_type &)98 inline 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
109 template <class T>
is_nan_helper(T,const boost::false_type &)110 inline bool is_nan_helper(T, const boost::false_type&)
111 {
112 return false;
113 }
114
115 }
116
117 namespace math{
118
119 namespace detail{
120
121 #ifdef BOOST_MATH_USE_STD_FPCLASSIFY
122 template <class T>
BOOST_NO_MACRO_EXPAND(T t,const native_tag &)123 inline int fpclassify_imp BOOST_NO_MACRO_EXPAND(T t, const native_tag&)
124 {
125 return (std::fpclassify)(t);
126 }
127 #endif
128
129 template <class T>
BOOST_NO_MACRO_EXPAND(T t,const generic_tag<true> &)130 inline 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
165 template <class T>
BOOST_NO_MACRO_EXPAND(T t,const generic_tag<false> &)166 inline 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
181 template<class T>
BOOST_NO_MACRO_EXPAND(T x,ieee_copy_all_bits_tag)182 int 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
210 template<class T>
BOOST_NO_MACRO_EXPAND(T x,ieee_copy_leading_bits_tag)211 int 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)
238 template <>
BOOST_NO_MACRO_EXPAND(long double t,const native_tag &)239 inline 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
247 template <class T>
BOOST_NO_MACRO_EXPAND(T t)248 inline 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
262 namespace detail {
263
264 #ifdef BOOST_MATH_USE_STD_FPCLASSIFY
265 template<class T>
isfinite_impl(T x,native_tag const &)266 inline bool isfinite_impl(T x, native_tag const&)
267 {
268 return (std::isfinite)(x);
269 }
270 #endif
271
272 template<class T>
isfinite_impl(T x,generic_tag<true> const &)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>
isfinite_impl(T x,generic_tag<false> const &)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>
isfinite_impl(T x,ieee_tag const &)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)
301 template <>
BOOST_NO_MACRO_EXPAND(long double t,const native_tag &)302 inline 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
310 template<class T>
311 inline 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
322 namespace detail {
323
324 #ifdef BOOST_MATH_USE_STD_FPCLASSIFY
325 template<class T>
isnormal_impl(T x,native_tag const &)326 inline bool isnormal_impl(T x, native_tag const&)
327 {
328 return (std::isnormal)(x);
329 }
330 #endif
331
332 template<class T>
isnormal_impl(T x,generic_tag<true> const &)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>
isnormal_impl(T x,generic_tag<false> const &)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>
isnormal_impl(T x,ieee_tag const &)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)
361 template <>
BOOST_NO_MACRO_EXPAND(long double t,const native_tag &)362 inline 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
370 template<class T>
371 inline 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
382 namespace detail {
383
384 #ifdef BOOST_MATH_USE_STD_FPCLASSIFY
385 template<class T>
isinf_impl(T x,native_tag const &)386 inline bool isinf_impl(T x, native_tag const&)
387 {
388 return (std::isinf)(x);
389 }
390 #endif
391
392 template<class T>
isinf_impl(T x,generic_tag<true> const &)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>
isinf_impl(T x,generic_tag<false> const &)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>
isinf_impl(T x,ieee_copy_all_bits_tag const &)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>
isinf_impl(T x,ieee_copy_leading_bits_tag const &)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)
439 template <>
BOOST_NO_MACRO_EXPAND(long double t,const native_tag &)440 inline 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
448 template<class T>
449 inline 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
460 namespace detail {
461
462 #ifdef BOOST_MATH_USE_STD_FPCLASSIFY
463 template<class T>
isnan_impl(T x,native_tag const &)464 inline bool isnan_impl(T x, native_tag const&)
465 {
466 return (std::isnan)(x);
467 }
468 #endif
469
470 template<class T>
isnan_impl(T x,generic_tag<true> const &)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>
isnan_impl(T x,generic_tag<false> const &)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>
isnan_impl(T x,ieee_copy_all_bits_tag const &)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>
isnan_impl(T x,ieee_copy_leading_bits_tag const &)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
519 template<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
BOOST_NO_MACRO_EXPAND(float t)528 template <> inline bool isnan BOOST_NO_MACRO_EXPAND<float>(float t){ return ::boost::math_detail::is_nan_helper(t, boost::true_type()); }
BOOST_NO_MACRO_EXPAND(double t)529 template <> inline bool isnan BOOST_NO_MACRO_EXPAND<double>(double t){ return ::boost::math_detail::is_nan_helper(t, boost::true_type()); }
BOOST_NO_MACRO_EXPAND(long double t)530 template <> 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