#include #include #include #include #if defined(_MSC_VER) || defined(__BORLANDC__) #include #endif #ifdef BOOST_NO_STDC_NAMESPACE namespace std{ using ::abs; using ::fabs; } #endif #ifndef FP_NORMAL #define FP_ZERO 0 #define FP_NORMAL 1 #define FP_INFINITE 2 #define FP_NAN 3 #define FP_SUBNORMAL 4 #else #define NATIVE_VALUES #endif namespace pending{ #define BOOST_NO_MACRO_EXPAND /**/ template int fpclassify BOOST_NO_MACRO_EXPAND(T t) { // whenever possible check for Nan's first: #ifdef isnan if(isnan(t)) return FP_NAN; #elif defined(_MSC_VER) || defined(__BORLANDC__) if(::_isnan(t)) return FP_NAN; #endif // std::fabs broken on a few systems especially for long long!!!! T at = (t < T(0)) ? -t : t; // Use a process of exclusion to figure out // what kind of type we have, this relies on // IEEE conforming reals that will treat // Nan's as unordered. Some compilers // don't do this once optimisations are // turned on, hence the check for nan's above. if(at <= (std::numeric_limits::max)()) { if(at >= (std::numeric_limits::min)()) return FP_NORMAL; return at ? FP_SUBNORMAL : FP_ZERO; } else if(at > (std::numeric_limits::max)()) return FP_INFINITE; return FP_NAN; } #ifdef fpclassify int fpclassify BOOST_NO_MACRO_EXPAND(float t) { return fpclassify(t); } int fpclassify BOOST_NO_MACRO_EXPAND(double t) { return fpclassify(t); } #ifndef __HP_aCC // The native fpclassify broken for long doubles with aCC // use portable one instead.... int fpclassify BOOST_NO_MACRO_EXPAND(long double t) { return fpclassify(t); } #endif #elif defined(_MSC_VER) // This only works for type double, for both float // and long double it gives misleading answers. int fpclassify BOOST_NO_MACRO_EXPAND(double t) { switch(::_fpclass(t)) { case _FPCLASS_SNAN /* Signaling NaN */ : case _FPCLASS_QNAN /* Quiet NaN */ : return FP_NAN; case _FPCLASS_NINF /*Negative infinity ( –INF) */ : case _FPCLASS_PINF /* Positive infinity (+INF) */ : return FP_INFINITE; case _FPCLASS_NN /* Negative normalized non-zero */ : case _FPCLASS_PN /* Positive normalized non-zero */ : return FP_NORMAL; case _FPCLASS_ND /* Negative denormalized */: case _FPCLASS_PD /* Positive denormalized */ : return FP_SUBNORMAL; case _FPCLASS_NZ /* Negative zero ( – 0) */ : case _FPCLASS_PZ /* Positive 0 (+0) */ : return FP_ZERO; default: /**/ ; } return FP_NAN; // should never get here!!! } #endif } template void test_classify(T t, const char* type) { std::cout << "Testing type " << type << std::endl; t = 2; T u = 2; BOOST_CHECK_EQUAL((::pending::fpclassify)(t), (int)FP_NORMAL); BOOST_CHECK_EQUAL((::pending::fpclassify)(-t), (int)FP_NORMAL); t = (std::numeric_limits::max)(); BOOST_CHECK_EQUAL((::pending::fpclassify)(t), (int)FP_NORMAL); BOOST_CHECK_EQUAL((::pending::fpclassify)(-t), (int)FP_NORMAL); t = (std::numeric_limits::min)(); BOOST_CHECK_EQUAL((::pending::fpclassify)(t), (int)FP_NORMAL); BOOST_CHECK_EQUAL((::pending::fpclassify)(-t), (int)FP_NORMAL); if(std::numeric_limits::has_denorm) { t /= 2; BOOST_CHECK_EQUAL((::pending::fpclassify)(t), (int)FP_SUBNORMAL); BOOST_CHECK_EQUAL((::pending::fpclassify)(-t), (int)FP_SUBNORMAL); } t = 0; BOOST_CHECK_EQUAL((::pending::fpclassify)(t), (int)FP_ZERO); BOOST_CHECK_EQUAL((::pending::fpclassify)(-t), (int)FP_ZERO); t /= -u; // create minus zero if it exists BOOST_CHECK_EQUAL((::pending::fpclassify)(t), (int)FP_ZERO); BOOST_CHECK_EQUAL((::pending::fpclassify)(-t), (int)FP_ZERO); // inifinity: if(std::numeric_limits::has_infinity) { // At least one std::numeric_limits::infinity)() returns zero // (Compaq true64 cxx), hence the check. t = (std::numeric_limits::infinity)(); BOOST_CHECK_EQUAL((::pending::fpclassify)(t), (int)FP_INFINITE); BOOST_CHECK_EQUAL((::pending::fpclassify)(-t), (int)FP_INFINITE); } #if !defined(__BORLANDC__) && !defined(__DECCXX) // divide by zero on Borland triggers a C++ exception :-( // divide by zero on Compaq CXX triggers a C style signal :-( t = 2; u = 0; t /= u; BOOST_CHECK_EQUAL((::pending::fpclassify)(t), (int)FP_INFINITE); BOOST_CHECK_EQUAL((::pending::fpclassify)(-t), (int)FP_INFINITE); t = -2; t /= u; BOOST_CHECK_EQUAL((::pending::fpclassify)(t), (int)FP_INFINITE); BOOST_CHECK_EQUAL((::pending::fpclassify)(-t), (int)FP_INFINITE); #endif #ifndef __BORLANDC__ // NaN's: // Note that Borland throws an exception if we even try to obtain a Nan // by calling std::numeric_limits::quiet_NaN() !!!!!!! if(std::numeric_limits::has_quiet_NaN) { t = std::numeric_limits::quiet_NaN(); BOOST_CHECK_EQUAL((::pending::fpclassify)(t), (int)FP_NAN); BOOST_CHECK_EQUAL((::pending::fpclassify)(-t), (int)FP_NAN); } if(std::numeric_limits::has_signaling_NaN) { t = std::numeric_limits::signaling_NaN(); BOOST_CHECK_EQUAL((::pending::fpclassify)(t), (int)FP_NAN); BOOST_CHECK_EQUAL((::pending::fpclassify)(-t), (int)FP_NAN); } #endif } int test_main(int, char* [] ) { // start by printing some information: #ifdef isnan std::cout << "Platform has isnan macro." << std::endl; #endif #ifdef fpclassify std::cout << "Platform has fpclassify macro." << std::endl; #endif #ifdef NATIVE_VALUES std::cout << "Platform has FP_NORMAL macro." << std::endl; #endif std::cout << "FP_ZERO: " << (int)FP_ZERO << std::endl; std::cout << "FP_NORMAL: " << (int)FP_NORMAL << std::endl; std::cout << "FP_INFINITE: " << (int)FP_INFINITE << std::endl; std::cout << "FP_NAN: " << (int)FP_NAN << std::endl; std::cout << "FP_SUBNORMAL: " << (int)FP_SUBNORMAL << std::endl; // then run the tests: test_classify(float(0), "float"); test_classify(double(0), "double"); test_classify((long double)(0), "long double"); return 0; }