| /*********************************************************************** |
| * |
| * 18.numeric.special.float.cpp |
| * |
| * test to exercise floating point specializations of the numeric_limits |
| * class template |
| * |
| * $Id$ |
| * |
| *********************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed |
| * with this work for additional information regarding copyright |
| * ownership. The ASF licenses this file to you under the Apache |
| * License, Version 2.0 (the "License"); you may not use this file |
| * except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| * implied. See the License for the specific language governing |
| * permissions and limitations under the License. |
| * |
| * Copyright 2000-2008 Rogue Wave Software, Inc. |
| * |
| ***********************************************************************/ |
| |
| #include <rw/_defs.h> |
| |
| #if defined (__IBMCPP__) && !defined (_RWSTD_NO_IMPLICIT_INCLUSION) |
| // disable implicit inclusion to work around |
| // a limitation in IBM's VisualAge 5.0.2.0 (see PR#26959) |
| # define _RWSTD_NO_IMPLICIT_INCLUSION |
| #endif |
| |
| |
| #include <limits> |
| |
| #include <climits> |
| #include <cstdlib> // for atof |
| #include <cstdio> // for sprintf |
| |
| #include <float.h> // for constants, MSVC _fpclass, _isinf, _isnan |
| #include <math.h> // for fpclassify, isinf, isnan |
| |
| #ifndef _RWSTD_NO_IEEEFP_H |
| # include <ieeefp.h> // for fpclass, isnan |
| #endif // _RWSTD_NO_IEEEFP_H |
| |
| #include <rw_driver.h> |
| |
| // use FLT_ROUNDS if available, otherwise (e.g., when <ieeefp.h> must |
| // be #included to bring the macro in), fall back on our own macro |
| #ifndef FLT_ROUNDS |
| # define FLT_ROUNDS _RWSTD_FLT_ROUNDS |
| #endif // FLT_ROUNDS |
| |
| /***********************************************************************/ |
| |
| // determine whether the architecture is little-endian or big-endian |
| static const int endian_test = 1; |
| |
| // extern works around a SunPro 5.4 bug (PR #28124) |
| extern const bool big_endian = !*(const char*)&endian_test; |
| |
| /***********************************************************************/ |
| |
| volatile float flt_zero = 0; |
| volatile double dbl_zero = 0; |
| |
| #ifndef _RWSTD_NO_LONG_DOUBLE |
| |
| volatile long double ldbl_zero = 0; |
| |
| #endif // _RWSTD_NO_LONG_DOUBLE |
| |
| /***********************************************************************/ |
| |
| #if defined (_MSC_VER) |
| |
| const char* fpclass_name (int fpc) |
| { |
| switch (fpc) { |
| case _FPCLASS_SNAN: return "_FPCLASS_SNAN"; |
| case _FPCLASS_QNAN: return "_FPCLASS_QNAN"; |
| case _FPCLASS_NINF: return "_FPCLASS_NINF"; |
| case _FPCLASS_NN: return "_FPCLASS_NN"; |
| case _FPCLASS_ND: return "_FPCLASS_ND"; |
| case _FPCLASS_NZ: return "_FPCLASS_NZ"; |
| case _FPCLASS_PZ: return "_FPCLASS_PZ"; |
| case _FPCLASS_PD: return "_FPCLASS_PD"; |
| case _FPCLASS_PN: return "_FPCLASS_PN"; |
| case _FPCLASS_PINF: return "_FPCLASS_PINF"; |
| } |
| |
| static char buf [16]; |
| std::sprintf (buf, "%#x", fpc); |
| return buf; |
| } |
| |
| #elif defined (_RWSTD_OS_SUNOS) |
| |
| const char* fpclass_name (fpclass_t fpc) |
| { |
| switch (fpc) { |
| case FP_SNAN: return "FP_SNAN"; |
| case FP_QNAN: return "FP_QNAN"; |
| case FP_NINF: return "FP_NINF"; |
| case FP_PINF: return "FP_PINF"; |
| case FP_NDENORM: return "FP_NDENORM"; |
| case FP_PDENORM: return "FP_PDENORM"; |
| case FP_NZERO: return "FP_NZERO"; |
| case FP_PZERO: return "FP_PZERO"; |
| case FP_NNORM: return "FP_NNORM"; |
| case FP_PNORM: return "FP_PNORM"; |
| |
| } |
| |
| static char buf [16]; |
| std::sprintf (buf, "%#x", fpc); |
| return buf; |
| } |
| |
| #elif defined (fpclassify) // C99 classification |
| |
| const char* fpclass_name (int fpc) |
| { |
| switch (fpc) { |
| # ifdef FP_INFINITE |
| case FP_INFINITE: return "FP_INFINITE"; |
| # endif |
| # ifdef FP_NAN |
| case FP_NAN: return "FP_NAN"; |
| # endif |
| # ifdef FP_NORMAL |
| case FP_NORMAL: return "FP_NORMAL"; |
| # endif |
| # ifdef FP_SUBNORMAL |
| case FP_SUBNORMAL: return "FP_SUBNORMAL"; |
| # endif |
| # ifdef FP_ZERO |
| case FP_ZERO: return "FP_ZERO"; |
| # endif |
| default: break; |
| } |
| |
| static char buf [16]; |
| std::sprintf (buf, "%#x", fpc); |
| return buf; |
| } |
| |
| #endif |
| |
| /***********************************************************************/ |
| |
| #ifdef _MSC_VER |
| |
| inline int _fpclass (float val) |
| { |
| long lval = *_RWSTD_REINTERPRET_CAST (long*, &val); |
| |
| if (0 == (lval & 0x7FFFFFFF)) |
| return lval ? _FPCLASS_NZ : _FPCLASS_PZ; |
| |
| if (0 == (lval & 0x7F800000)) |
| return (lval & 0x80000000) ? _FPCLASS_ND : _FPCLASS_PD; |
| |
| if (0x7F800000 == (lval & 0x7FFFFFFF)) |
| return (lval & 0x80000000) ? _FPCLASS_NINF : _FPCLASS_PINF; |
| |
| if (0x7F800000 == (lval & 0x7F800000)) |
| return (lval & 0x00400000) ? _FPCLASS_QNAN : _FPCLASS_SNAN; |
| |
| return (lval & 0x80000000) ? _FPCLASS_NN : _FPCLASS_PN; |
| } |
| |
| inline int _fpclass (long double val) |
| { |
| return _fpclass (double (val)); |
| } |
| |
| #endif |
| |
| template <class FloatT> |
| void test_infinity (FloatT inf, FloatT max, const char *tname) |
| { |
| if (!std::numeric_limits<FloatT>::traps) { |
| |
| // infinity must be even greater than the maximum value |
| rw_assert (inf > max, 0, __LINE__, |
| "numeric_limits<%s>::infinity() > " |
| "numeric_limits<%s>::max()", tname, tname); |
| |
| // multiplying infinity by anything other than 0.0 yields infinity |
| rw_assert (inf == inf * inf, 0, __LINE__, |
| "numeric_limits<%s>::infinity()", tname); |
| } |
| |
| #ifdef _MSC_VER |
| |
| const int fpc = _fpclass (inf); |
| rw_assert (_FPCLASS_PINF == fpc, 0, __LINE__, |
| "_fpclass (numeric_limits<%s>::infinity()) == " |
| "%d (_FPCLASS_PINF), got %d (%s)", |
| tname, _FPCLASS_PINF, fpc, fpclass_name (fpc)); |
| |
| #elif defined (_RWSTD_OS_SUNOS) |
| |
| rw_assert (!finite (inf), 0, __LINE__, |
| "finite (numeric_limits<%s>::infinity()) == 0, " |
| "got non-zero", tname); |
| |
| const fpclass_t fpc = fpclass (inf); |
| rw_assert (FP_PINF == fpclass (inf), 0, __LINE__, |
| "fpclass (numeric_limits<%s>::infinity()) == %d (FP_PINF), " |
| "got %d (%s)", tname, FP_PINF, fpc, fpclass_name (fpc)); |
| |
| #else |
| |
| # ifdef isinf |
| |
| rw_assert (isinf (inf), 0, __LINE__, |
| "isinf (numeric_limits<%s>::infinity()) != 0, got 0", |
| tname); |
| |
| # endif // isinf |
| |
| # ifdef fpclassify |
| |
| const int fpc = fpclassify (inf); |
| rw_assert (FP_INFINITE == fpc, 0, __LINE__, |
| "fpclassify(numeric_limits<%s>::infinity()) == " |
| "%d (FP_INFINITE), got %d (%s)", tname, |
| FP_INFINITE, fpc, fpclass_name (fpc)); |
| |
| # endif // fpclassify |
| |
| #endif |
| |
| } |
| |
| /***********************************************************************/ |
| |
| template <class FloatT> |
| void test_quiet_NaN (FloatT qnan, FloatT qnan2, const char *tname) |
| { |
| // NAN never compares equal to self or any other number |
| rw_assert (!(qnan == qnan2), 0, __LINE__, |
| "numeric_limits<%s>::quiet_NaN() != " |
| "numeric_limits<%1$s>::quiet_NaN()", |
| tname); |
| |
| const FloatT inf = std::numeric_limits<FloatT>::infinity (); |
| |
| rw_assert (!(qnan == inf), 0, __LINE__, |
| "numeric_limits<%s>::quiet_NaN() != " |
| "numeric_limits<%1$s>::infinity()", |
| tname); |
| |
| rw_assert (!(qnan == -inf), 0, __LINE__, |
| "numeric_limits<%s>::quiet_NaN() != " |
| "-numeric_limits<%1$s>::infinity()", |
| tname); |
| |
| #ifdef _MSC_VER |
| |
| rw_assert (0 != _isnan (qnan), 0, __LINE__, |
| "_isnan(numeric_limits<%s>::quiet_NaN()) != 0, got 0", |
| tname); |
| |
| const int fpc = _fpclass (qnan); |
| rw_assert (_FPCLASS_QNAN == fpc, 0, __LINE__, |
| "_fpclass(numeric_limits<%s>::quiet_NaN()) == " |
| "%d (_FPCLASS_QNAN), got %d (%s)", |
| tname, _FPCLASS_QNAN, fpc, fpclass_name (fpc)); |
| |
| #elif defined (_RWSTD_OS_SUNOS) |
| |
| rw_assert (!finite (qnan), 0, __LINE__, |
| "finite(numeric_limits<%s>::quiet_NaN()) == 0, " |
| "got non-zero", tname); |
| |
| const fpclass_t fpc = fpclass (qnan); |
| rw_assert (FP_QNAN == fpc, 0, __LINE__, |
| "fpclass(numeric_limits<%s>::infinity()) == %d (FP_QNAN), " |
| "got %d (%s)", tname, FP_QNAN, fpc, fpclass_name (fpc)); |
| |
| #else |
| |
| # ifdef isnan |
| |
| rw_assert (0 != isnan (qnan), 0, __LINE__, |
| "isnan(numeric_limits<%s>::quiet_NaN()) != 0, got 0", |
| tname); |
| |
| # endif // isnan |
| |
| # ifdef fpclassify |
| |
| const int fpc = fpclassify (qnan); |
| rw_assert (FP_NAN == fpc, 0, __LINE__, |
| "fpclassify(numeric_limits<%s>::quiet_NaN()) == " |
| "%d (FP_NAN), got %d (%s)", tname, |
| FP_NAN, fpc, fpclass_name (fpc)); |
| |
| # endif // fpclassify |
| |
| #endif |
| |
| } |
| |
| /***********************************************************************/ |
| |
| template <class FloatT> |
| void test_signaling_NaN (FloatT snan, FloatT snan2, const char *tname) |
| { |
| // NAN never compares equal to self or any other number |
| rw_assert (!(snan == snan2), 0, __LINE__, |
| "numeric_limits<%s>::signaling_NaN() != " |
| "numeric_limits<%1$s>::signaling_NaN()", |
| tname); |
| |
| const FloatT inf = std::numeric_limits<FloatT>::infinity (); |
| |
| rw_assert (!(snan == inf), 0, __LINE__, |
| "numeric_limits<%s>::signaling_NaN() != " |
| "numeric_limits<%1$s>::infinity()", |
| tname); |
| |
| rw_assert (!(snan == -inf), 0, __LINE__, |
| "numeric_limits<%s>::signaling_NaN() != " |
| "-numeric_limits<%1$s>::infinity()", |
| tname); |
| |
| #ifdef _MSC_VER |
| |
| rw_assert (0 != _isnan (snan), 0, __LINE__, |
| "_isnan (numeric_limits<%s>::signaling_NaN()) != 0, got 0", |
| tname); |
| |
| const int fpc = _fpclass (snan); |
| rw_assert (_FPCLASS_SNAN == fpc, 0, __LINE__, |
| "_fpclass(numeric_limits<%s>::signaling_NaN()) == " |
| "%d (_FPCLASS_SNAN), got %d (%s)", |
| tname, _FPCLASS_SNAN, fpc, fpclass_name (fpc)); |
| |
| #elif defined (_RWSTD_OS_SUNOS) |
| |
| rw_assert (!finite (snan), 0, __LINE__, |
| "finite(numeric_limits<%s>::signaling_NaN()) == 0, " |
| "got non-zero", tname); |
| |
| const fpclass_t fpc = fpclass (snan); |
| rw_assert (FP_SNAN == fpc, 0, __LINE__, |
| "fpclass(numeric_limits<%s>::signaling_NaN()) == %d " |
| "(FP_SNAN), got %d (%s)", tname, |
| FP_SNAN, fpc, fpclass_name (fpc)); |
| #else |
| |
| # ifdef isnan |
| |
| rw_assert (0 != isnan (snan), 0, __LINE__, |
| "isnan(numeric_limits<%s>::signaling_NaN()) != 0, got 0", |
| tname); |
| |
| # endif // isnan |
| |
| # ifdef fpclassify |
| |
| const int fpc = fpclassify (snan); |
| rw_assert (FP_NAN == fpc, 0, __LINE__, |
| "fpclassify(numeric_limits<%s>::signaling_NaN()) == " |
| "%d (FP_NAN), got %d (%s)", tname, |
| FP_NAN, fpc, fpclass_name (fpc)); |
| |
| # endif // fpclassify |
| |
| #endif |
| |
| } |
| |
| /***********************************************************************/ |
| |
| template <class T> |
| struct limits_values; |
| |
| #if defined (__alpha) && !defined (__linux__) \ |
| && !defined (__VMS) && defined (__DECCXX) && defined (__PURE_CNAME) |
| |
| extern "C" unsigned int read_rnd (); |
| #define read_rnd read_rnd |
| |
| #endif // __alpha && !__linux__ && !__VMS && __DECCXX && __PURE_CNAME |
| |
| /***********************************************************************/ |
| |
| _RWSTD_SPECIALIZED_CLASS |
| struct limits_values<float> |
| { |
| static bool is_specialized () { return true; } |
| |
| static float (min) () { return FLT_MIN; } |
| static float (max) () { return FLT_MAX; } |
| |
| static int digits () { return FLT_MANT_DIG; } |
| static int digits10 () { return FLT_DIG; } |
| static bool is_signed () { return 0.0f - 1.0f < 0.0f; } |
| static bool is_integer () { return false; } |
| static bool is_exact () { return false; } |
| static int radix () { return FLT_RADIX; } |
| |
| static float epsilon () { return FLT_EPSILON; } |
| |
| static float round_error () { |
| |
| #ifdef __osf__ |
| int rounding = FLT_ROUNDS; |
| # ifdef read_rnd |
| if (rounding == std::round_indeterminate) |
| rounding = _RWSTD_STATIC_CAST (int, read_rnd ()); |
| # endif // read_rnd |
| |
| switch (rounding) { |
| case std::round_toward_zero: |
| case std::round_toward_infinity: |
| case std::round_toward_neg_infinity: |
| return 1.0f; |
| default: |
| return 0.5f; |
| } |
| #else |
| return 0.5f; |
| #endif |
| |
| } |
| |
| static int min_exponent () { return FLT_MIN_EXP; } |
| static int min_exponent10 () { return FLT_MIN_10_EXP; } |
| static int max_exponent () { return FLT_MAX_EXP; } |
| static int max_exponent10 () { return FLT_MAX_10_EXP; } |
| |
| static bool has_infinity () { |
| return true; |
| } |
| |
| static bool has_quiet_NaN () { |
| |
| #if defined (NAN) |
| // 7.12, p5 of C99: |
| // The macro NAN is defined if and only if the implementation |
| // supports quiet NaNs for the float type. It expands to a |
| // constant expression of type float representing a quiet NaN. |
| return true; |
| #else |
| return true; |
| #endif |
| |
| } |
| |
| |
| static bool has_signaling_NaN () { |
| |
| #if defined (FLT_SNAN) |
| return true; |
| #else |
| # ifndef _RWSTD_NO_SIGNALING_NAN |
| return true; |
| # else |
| return false; |
| # endif // _RWSTD_NO_SIGNALING_NAN |
| #endif |
| |
| } |
| |
| |
| static std::float_denorm_style has_denorm () { |
| |
| #if defined (_AIX) \ |
| || defined (__hpux) \ |
| || defined (__osf__) \ |
| || defined (_MSC_VER) |
| return std::denorm_present; |
| #else |
| return std::denorm_indeterminate; |
| #endif |
| } |
| |
| |
| static bool has_denorm_loss () { return false; } |
| |
| |
| static float infinity () { |
| |
| #if defined (FLT_INFINITY) |
| return FLT_INFINITY; |
| #elif defined (_RWSTD_NO_DBL_TRAPS) |
| |
| float inf = float (std::atof ("inf")); |
| if (inf > FLT_MAX) |
| return inf; |
| |
| inf = 1.0f / flt_zero; |
| |
| return inf; |
| |
| #else |
| const union { |
| char bits [sizeof (float)]; |
| float val; |
| } val = { |
| _RWSTD_FLT_INF_BITS |
| }; |
| |
| return val.val; |
| #endif |
| |
| } |
| |
| |
| static float quiet_NaN () { |
| |
| #if defined (NAN) |
| return NAN; |
| #elif defined (FLT_QNAN) |
| return FLT_QNAN; |
| #elif defined (_RWSTD_NO_DBL_TRAPS) |
| |
| float nan = float (std::atof ("nan")); |
| |
| if (nan != float (std::atof ("nan"))) |
| return nan; |
| |
| nan = float (dbl_zero / flt_zero); |
| |
| return nan; |
| |
| #else |
| const union { |
| char bits [sizeof (float)]; |
| float val; |
| } val = { |
| _RWSTD_FLT_QNAN_BITS |
| }; |
| |
| return val.val; |
| |
| #endif |
| |
| } |
| |
| |
| static float signaling_NaN () { |
| |
| #if defined (FLT_SNAN) |
| return FLT_SNAN; |
| #elif defined (_RWSTD_NO_DBL_TRAPS) |
| |
| union { |
| float val; |
| char bits [sizeof (float)]; |
| } snan; |
| |
| snan.val = infinity (); |
| |
| # ifdef __hpux |
| |
| if (big_endian) { |
| snan.bits [sizeof snan.val - 2] = '\xc0'; |
| } |
| else { |
| snan.bits [1] = '\xc0'; |
| } |
| |
| # else // if !defined (__hpux) |
| |
| // convert infinity into a signaling NAN |
| // (toggle any bit in signifcand) |
| if (big_endian) { |
| snan.bits [sizeof snan.val - 1] |= 1; |
| } |
| else { |
| snan.bits [0] |= 1; |
| } |
| |
| # endif // __hpux |
| |
| return snan.val; |
| |
| #else |
| |
| const union { |
| char bits [sizeof (float)]; |
| float val; |
| } val = { |
| _RWSTD_FLT_SNAN_BITS |
| }; |
| |
| return val.val; |
| |
| #endif |
| |
| } |
| |
| |
| static float denorm_min () { |
| |
| #if defined (__FLT_DENORM_MIN__) |
| // gcc 3.x predefined macro |
| return __FLT_DENORM_MIN__; |
| #elif defined (__osf__) |
| const unsigned short pos_denorm [] = { 0x0001, 0x0000 }; |
| return *_RWSTD_REINTERPRET_CAST (const float*, pos_denorm); |
| #else |
| // assume IEEE 754 |
| |
| static union { |
| float denorm_min; |
| char denorm_min_bits [sizeof (float)]; |
| } u; |
| |
| if (big_endian) |
| u.denorm_min_bits [sizeof (float) - 1] = '\001'; |
| else |
| u.denorm_min_bits [0] = '\001'; |
| |
| return u.denorm_min; |
| |
| #endif |
| |
| } |
| |
| |
| static bool is_iec559 () { |
| |
| #if defined (__osf__) |
| |
| // Tru64 UNIX with Compaq C++: IEC 559 support is enabled |
| // by specifying the -ieee flag on the compiler command line |
| |
| # ifdef _IEEE_FP |
| return true; |
| # else |
| return false; |
| # endif // _IEEE_FP |
| #else |
| return true; |
| #endif |
| |
| } |
| |
| |
| static bool is_bounded () { return true; } |
| static bool is_modulo () { return false; } |
| |
| static bool traps () { return true; } |
| static bool tinyness_before () { return false; } |
| |
| |
| static std::float_round_style round_style () { |
| |
| #if defined (_AIX) |
| return std::round_to_nearest; |
| #else |
| return _RWSTD_STATIC_CAST (std::float_round_style, FLT_ROUNDS); |
| #endif |
| } |
| |
| }; |
| |
| /***********************************************************************/ |
| |
| |
| _RWSTD_SPECIALIZED_CLASS |
| struct limits_values<double> |
| { |
| static bool is_specialized () { return true; } |
| |
| static double (min)() { return DBL_MIN; } |
| static double (max)() { return DBL_MAX; } |
| |
| static int digits () { return DBL_MANT_DIG; } |
| static int digits10 () { return DBL_DIG; } |
| static bool is_signed () { return 0.0 - 1.0 < 0.0; } |
| static bool is_integer () { return false; } |
| static bool is_exact () { return false; } |
| static int radix () { return FLT_RADIX; } |
| |
| static double epsilon () { return DBL_EPSILON; } |
| |
| |
| static double round_error () { |
| |
| #ifdef __osf__ |
| int rounding = FLT_ROUNDS; |
| # ifdef read_rnd |
| if (rounding == std::round_indeterminate) |
| rounding = _RWSTD_STATIC_CAST (int, read_rnd ()); |
| # endif // read_rnd |
| switch (rounding) { |
| case std::round_toward_zero: |
| case std::round_toward_infinity: |
| case std::round_toward_neg_infinity: |
| return 1.0; |
| default: |
| return 0.5; |
| } |
| #else |
| return 0.5; |
| #endif |
| |
| } |
| |
| |
| static int min_exponent () { return DBL_MIN_EXP; } |
| static int min_exponent10 () { return DBL_MIN_10_EXP; } |
| static int max_exponent () { return DBL_MAX_EXP; } |
| static int max_exponent10 () { return DBL_MAX_10_EXP; } |
| |
| |
| static bool has_infinity () { |
| return true; |
| } |
| |
| |
| static bool has_quiet_NaN () { |
| return true; |
| } |
| |
| |
| static bool has_signaling_NaN () { |
| |
| #if defined DBL_SNAN |
| return true; |
| #else |
| # ifndef _RWSTD_NO_SIGNALING_NAN |
| return true; |
| # else |
| return false; |
| # endif // _RWSTD_NO_SIGNALING_NAN |
| #endif |
| |
| } |
| |
| |
| static std::float_denorm_style has_denorm () { |
| #if defined (_AIX) \ |
| || defined (__hpux) \ |
| || defined (__osf__) \ |
| || defined (_MSC_VER) |
| return std::denorm_present; |
| #else |
| return std::denorm_indeterminate; |
| #endif |
| } |
| |
| |
| static bool has_denorm_loss () { return false; } |
| |
| |
| static double infinity () { |
| |
| #if defined (DBL_INFINITY) |
| return DBL_INFINITY; |
| #elif defined (_RWSTD_NO_DBL_TRAPS) |
| |
| double inf = std::atof ("inf"); |
| if (inf > DBL_MAX) |
| return inf; |
| |
| inf = 1.0 / dbl_zero; |
| |
| return inf; |
| |
| #else |
| const union { |
| char bits [sizeof (double)]; |
| double val; |
| } val = { |
| _RWSTD_DBL_INF_BITS |
| }; |
| |
| return val.val; |
| #endif |
| |
| } |
| |
| |
| static double quiet_NaN () { |
| |
| #if defined (NAN) |
| return NAN; |
| #elif defined (DBL_QNAN) |
| return DBL_QNAN; |
| #elif defined (_RWSTD_NO_DBL_TRAPS) |
| |
| double nan = std::atof ("nan"); |
| |
| if (nan != std::atof ("nan")) |
| return nan; |
| |
| nan = flt_zero / dbl_zero; |
| |
| return nan; |
| |
| #else |
| const union { |
| char bits [sizeof (double)]; |
| double val; |
| } val = { |
| _RWSTD_DBL_QNAN_BITS |
| }; |
| |
| return val.val; |
| |
| #endif |
| |
| } |
| |
| |
| static double signaling_NaN () { |
| |
| #if defined (DBL_SNAN) |
| return DBL_SNAN; |
| #elif defined (_RWSTD_NO_DBL_TRAPS) |
| |
| union { |
| double val; |
| char bits [sizeof (double)]; |
| } snan; |
| |
| snan.val = infinity (); |
| |
| # ifdef __hpux |
| |
| if (big_endian) { |
| snan.bits [sizeof snan.val - 2] = '\xf8'; |
| } |
| else { |
| snan.bits [1] = '\xf8'; |
| } |
| |
| # else // if !defined (__hpux) |
| |
| // convert infinity into a signaling NAN |
| // (toggle any bit in signifcand) |
| if (big_endian) { |
| snan.bits [sizeof snan.val - 1] |= 1; |
| } |
| else { |
| snan.bits [0] |= 1; |
| } |
| |
| # endif // __hpux |
| |
| return snan.val; |
| |
| #else |
| |
| const union { |
| char bits [sizeof (double)]; |
| double val; |
| } val = { |
| _RWSTD_DBL_SNAN_BITS |
| }; |
| |
| return val.val; |
| |
| #endif |
| |
| } |
| |
| |
| static double denorm_min () { |
| |
| #if defined (__DBL_DENORM_MIN__) |
| // gcc 3.x predefined macro |
| return __DBL_DENORM_MIN__; |
| #elif defined (__osf__) |
| const unsigned short pos_denorm [] = { 0x0001, 0x0000 }; |
| return *_RWSTD_REINTERPRET_CAST (const double*, pos_denorm); |
| #else |
| // assume IEEE 754 |
| |
| static union { |
| double denorm_min; |
| char denorm_min_bits [sizeof (double)]; |
| } u; |
| |
| if (big_endian) |
| u.denorm_min_bits [sizeof (double) - 1] = '\001'; |
| else |
| u.denorm_min_bits [0] = '\001'; |
| |
| return u.denorm_min; |
| |
| #endif |
| |
| } |
| |
| |
| static bool is_iec559 () { |
| |
| #if defined (__osf__) |
| # ifdef _IEEE_FP |
| return true; |
| # else |
| return false; |
| # endif // _IEEE_FP |
| #else |
| return true; |
| #endif |
| |
| } |
| |
| |
| static bool is_bounded () { return true; } |
| static bool is_modulo () { return false; } |
| |
| static bool traps () { return true; } |
| static bool tinyness_before () { return false; } |
| |
| |
| static std::float_round_style round_style () { |
| |
| #if defined (_AIX) |
| return std::round_to_nearest; |
| #else |
| return _RWSTD_STATIC_CAST (std::float_round_style, FLT_ROUNDS); |
| #endif |
| |
| } |
| |
| }; |
| |
| /***********************************************************************/ |
| |
| |
| _RWSTD_SPECIALIZED_CLASS |
| struct limits_values<long double> |
| { |
| static bool is_specialized () { return true; } |
| |
| static long double (min)() { return LDBL_MIN; } |
| static long double (max)() { return LDBL_MAX; } |
| |
| static int digits () { return LDBL_MANT_DIG; } |
| static int digits10 () { return LDBL_DIG; } |
| static bool is_signed () { return 0.0 - 1.0 < 0.0; } |
| static bool is_integer () { return false; } |
| static bool is_exact () { return false; } |
| static int radix () { return FLT_RADIX; } |
| |
| static long double epsilon () { return LDBL_EPSILON; } |
| |
| static long double round_error () { |
| |
| #ifdef __osf__ |
| int rounding = FLT_ROUNDS; |
| # ifdef read_rnd |
| if (rounding == std::round_indeterminate) |
| rounding = _RWSTD_STATIC_CAST (int, read_rnd ()); |
| # endif // read_rnd |
| switch (rounding) { |
| case std::round_toward_zero: |
| case std::round_toward_infinity: |
| case std::round_toward_neg_infinity: |
| return 1.0; |
| default: |
| return 0.5; |
| } |
| #else |
| return 0.5; |
| #endif |
| |
| } |
| |
| static int min_exponent () { return LDBL_MIN_EXP; } |
| static int min_exponent10 () { return LDBL_MIN_10_EXP; } |
| static int max_exponent () { return LDBL_MAX_EXP; } |
| static int max_exponent10 () { return LDBL_MAX_10_EXP; } |
| |
| static bool has_infinity () { |
| return true; |
| } |
| |
| static bool has_quiet_NaN () { |
| return true; |
| } |
| |
| |
| static bool has_signaling_NaN () { |
| |
| #if defined (LDBL_SNAN) |
| return true; |
| #else |
| # ifndef _RWSTD_NO_SIGNALING_NAN |
| return true; |
| # else |
| return false; |
| # endif // _RWSTD_NO_SIGNALING_NAN |
| #endif |
| |
| } |
| |
| |
| static std::float_denorm_style has_denorm () { |
| #if defined (_AIX) \ |
| || defined (__hpux) \ |
| || defined (__osf__) \ |
| || defined (_MSC_VER) |
| return std::denorm_present; |
| #else |
| return std::denorm_indeterminate; |
| #endif |
| } |
| |
| static bool has_denorm_loss () { return false; } |
| |
| |
| static long double infinity () { |
| |
| #if defined (LDBL_INFINITY) |
| return LDBL_INFINITY; |
| #elif defined (_RWSTD_NO_DBL_TRAPS) |
| |
| long double inf = std::atof ("inf"); |
| if (inf > LDBL_MAX) |
| return inf; |
| |
| inf = (long double)1.0 / ldbl_zero; |
| |
| return inf; |
| |
| #else |
| const union { |
| char bits [sizeof (long double)]; |
| long double val; |
| } val = { |
| _RWSTD_LDBL_INF_BITS |
| }; |
| |
| return val.val; |
| #endif |
| |
| } |
| |
| |
| static long double quiet_NaN () { |
| |
| #if defined (NAN) |
| return NAN; |
| #elif defined (LDBL_QNAN) |
| return LDBL_QNAN; |
| #elif defined (_RWSTD_NO_DBL_TRAPS) |
| |
| long double nan = std::atof ("nan"); |
| |
| if (nan != (long double)std::atof ("nan")) |
| return nan; |
| |
| nan = ldbl_zero / ldbl_zero; |
| |
| return nan; |
| |
| #else |
| const union { |
| char bits [sizeof (long double)]; |
| long double val; |
| } val = { |
| _RWSTD_LDBL_QNAN_BITS |
| }; |
| |
| return val.val; |
| |
| #endif |
| |
| } |
| |
| |
| static long double signaling_NaN () { |
| |
| #if defined (LDBL_SNAN) |
| return LDBL_SNAN; |
| #elif defined (_RWSTD_NO_DBL_TRAPS) |
| |
| union { |
| long double val; |
| char bits [sizeof (long double)]; |
| } snan; |
| |
| snan.val = infinity (); |
| |
| # ifdef __hpux |
| |
| if (big_endian) { |
| snan.bits [sizeof snan.val - 3] = '\x80'; |
| } |
| else { |
| snan.bits [2] = '\x80'; |
| } |
| |
| # else // if !defined (__hpux) |
| |
| // convert infinity into a signaling NAN |
| // (toggle any bit in signifcand) |
| if (big_endian) { |
| snan.bits [sizeof snan.val - 1] |= 1; |
| } |
| else { |
| snan.bits [0] |= 1; |
| } |
| |
| # endif // __hpux |
| |
| return snan.val; |
| |
| #else |
| |
| const union { |
| char bits [sizeof (long double)]; |
| long double val; |
| } val = { |
| _RWSTD_LDBL_SNAN_BITS |
| }; |
| |
| return val.val; |
| |
| #endif |
| |
| } |
| |
| |
| static long double denorm_min () { |
| |
| #if defined (__LDBL_DENORM_MIN__) |
| // gcc 3.x predefined macro |
| return __LDBL_DENORM_MIN__; |
| #elif defined (__osf__) |
| const unsigned short pos_denorm [] = { 0x0001, 0x0000 }; |
| return *_RWSTD_REINTERPRET_CAST (const long double*, pos_denorm); |
| #else |
| // assume IEEE 754 |
| |
| static union { |
| long double denorm_min; |
| char denorm_min_bits [sizeof (long double)]; |
| } u; |
| |
| if (big_endian) |
| u.denorm_min_bits [sizeof (long double) - 1] = '\001'; |
| else |
| u.denorm_min_bits [0] = '\001'; |
| |
| return u.denorm_min; |
| #endif |
| |
| } |
| |
| |
| static bool is_iec559 () { |
| #if defined (__osf__) |
| # ifdef _IEEE_FP |
| return true; |
| # else |
| return false; |
| # endif // _IEEE_FP |
| #else |
| return true; |
| #endif |
| } |
| |
| static bool is_bounded () { return true; } |
| static bool is_modulo () { return false; } |
| |
| static bool traps () { return true; } |
| static bool tinyness_before () { return false; } |
| |
| static std::float_round_style round_style () { |
| #if defined (_AIX) |
| return std::round_to_nearest; |
| #else |
| return _RWSTD_STATIC_CAST (std::float_round_style, FLT_ROUNDS); |
| #endif |
| } |
| }; |
| |
| /***********************************************************************/ |
| |
| template <class FloatT> |
| void test_limits (FloatT, const char *tname, const char *fmt) |
| { |
| typedef std::numeric_limits<FloatT> FLim; |
| typedef limits_values<FloatT> FVal; |
| |
| // self-test: assert that endianness is correctly computed |
| #if defined (__alpha) |
| rw_warn (!big_endian, 0, __LINE__, "alpha is typically little-endian"); |
| #elif defined (_AIX) |
| rw_warn (big_endian, 0, __LINE__, "AIX is big-endian"); |
| #elif defined (__hpux) && defined (_BIG_ENDIAN) |
| rw_warn (big_endian, 0, __LINE__, "HP-UX is big-endian"); |
| #elif defined (__i386__) |
| rw_warn (!big_endian, 0, __LINE__, "Intel x86 is little-endian"); |
| #elif defined (__sparc) |
| rw_warn (big_endian, 0, __LINE__, "SPARC is big-endian"); |
| #elif defined (_WIN64) |
| rw_warn (!big_endian, 0, __LINE__, "WIN64 is little-endian"); |
| #elif defined (_WIN32) |
| rw_warn (!big_endian, 0, __LINE__, "WIN32 is little-endian"); |
| #endif |
| |
| #ifndef _RWSTD_NO_STATIC_CONST_MEMBER_INIT |
| |
| # if !defined (__EDG__) || __EDG_VERSION__ > 245 |
| # define CHECK_CONST(const_int) \ |
| enum { e = const_int }; \ |
| (void)&const_int |
| # else // if EDG eccp < 3.0 |
| // working around an EDG eccp 2.4x ICE (not in 3.0) |
| # define CHECK_CONST(const_int) \ |
| switch (const_int) { case const_int: break; }; \ |
| (void)&const_int |
| # endif // __EDG__ |
| #else |
| // static const integral members must be usable |
| // as constant integral expressions |
| # define CHECK_CONST(const_int) enum { e = const_int } |
| #endif // _RWSTD_NO_STATIC_CONST_MEMBER_INIT |
| |
| |
| // compare against a computed value |
| #define VERIFY_DATA(member) do { \ |
| /* verify that member is a constant integral expression */ \ |
| CHECK_CONST (FLim::member); \ |
| /* verify value */ \ |
| rw_assert (FLim::member == FVal::member (), 0, __LINE__, \ |
| "numeric_limits<%s>::" #member " == %i, got %i", \ |
| tname, FVal::member (), FLim::member); \ |
| } while (0) |
| |
| // compare against a constant |
| #define VERIFY_CONST(member, value) do { \ |
| /* verify that member if a constant integral expression */ \ |
| CHECK_CONST (FLim::member); \ |
| /* verify value */ \ |
| rw_assert (FLim::member == value, 0, __LINE__, \ |
| "numeric_limits<%s>::" #member " == %i, got %i", \ |
| tname, FVal::member (), value); \ |
| } while (0) |
| |
| // account for NaN != NaN |
| #define VERIFY_FUNCTION(member) do { \ |
| /* verify function signature */ \ |
| FloatT (*pf)() _PTR_THROWS(()) = &FLim::member; \ |
| _RWSTD_UNUSED (pf); \ |
| /* verify value */ \ |
| rw_assert ( FLim::member () == FVal::member () \ |
| || FLim::member () != FLim::member () \ |
| && FVal::member () != FVal::member (), \ |
| 0, __LINE__, \ |
| "numeric_limits<%s>::" #member "() == %{@}, got %{@}", \ |
| tname, fmt, FVal::member (), fmt, FLim::member ()); \ |
| } while (0) |
| |
| #define VERIFY_SIGNATURE(member) do { \ |
| /* verify function signature */ \ |
| FloatT (*pf)() _PTR_THROWS(()) = &FLim::member; \ |
| _RWSTD_UNUSED (pf); \ |
| } while (0) |
| |
| #undef min |
| #undef max |
| |
| VERIFY_FUNCTION (min); // 18.2.1.2, p1 |
| VERIFY_FUNCTION (max); // p4 |
| VERIFY_DATA (digits); // p6 |
| VERIFY_DATA (digits10); // p9 |
| VERIFY_DATA (is_signed); // p11 |
| VERIFY_DATA (is_integer); // p13 |
| VERIFY_DATA (is_exact); // p15 |
| VERIFY_DATA (radix); // p17 |
| |
| VERIFY_FUNCTION (epsilon); // 18.2.1.2, p19 |
| VERIFY_FUNCTION (round_error); // p22 |
| |
| VERIFY_DATA (min_exponent); // 18.2.1.2, p23 |
| VERIFY_DATA (min_exponent10); // p25 |
| VERIFY_DATA (max_exponent); // p27 |
| VERIFY_DATA (max_exponent10); // p29 |
| |
| VERIFY_FUNCTION (infinity); // 18.2.1.2, p42 |
| |
| if (FLim::has_infinity) |
| test_infinity (FLim::infinity (), FLim::max (), tname); |
| |
| if (std::numeric_limits<FloatT>::traps) // p45 |
| VERIFY_SIGNATURE (quiet_NaN); |
| else { |
| VERIFY_FUNCTION (quiet_NaN); |
| |
| if (FLim::has_quiet_NaN) |
| test_quiet_NaN (FLim::quiet_NaN (), |
| FLim::quiet_NaN (), tname); |
| } |
| |
| if (std::numeric_limits<FloatT>::traps) // p47 |
| VERIFY_SIGNATURE (signaling_NaN); |
| else { |
| VERIFY_FUNCTION (signaling_NaN); |
| |
| if (FLim::has_signaling_NaN) |
| test_signaling_NaN (FLim::signaling_NaN (), |
| FLim::signaling_NaN (), tname); |
| } |
| |
| if (std::numeric_limits<FloatT>::traps) { // p49 |
| // denorm min may trap (e.g., Tru64 UNIX on Alpha |
| // with Compaq C++ without the -ieee compiler flag) |
| VERIFY_SIGNATURE (denorm_min); |
| } |
| else { |
| VERIFY_FUNCTION (denorm_min); |
| } |
| |
| #ifdef _MSC_VER |
| |
| if (sizeof (FloatT) > sizeof (float)) { |
| // for doubles and long doubles only, verify that denorm_min |
| // is properly classified |
| |
| const FloatT denorm_min = FLim::denorm_min (); |
| |
| const int fpc = _fpclass (double (denorm_min)); |
| |
| rw_assert (_FPCLASS_PD == fpc, 0, __LINE__, |
| "_fpclass(numeric_limits<%s>::denorm_min()) == " |
| "%d (_FPCLASS_PD), got %d (%s)", |
| tname, _FPCLASS_PD, fpc, fpclass_name (fpc)); |
| } |
| |
| #endif // _MSC_VER |
| |
| VERIFY_DATA (is_iec559); // 18.2.1.2, p52 |
| VERIFY_DATA (is_bounded); // p54 |
| VERIFY_DATA (is_modulo); // p56 |
| |
| VERIFY_DATA (tinyness_before); // 18.2.1.2, p61 |
| VERIFY_DATA (round_style); // p63 |
| |
| if (!FLim::traps) { // 18.2.1.2, p59 |
| // invalid floating point operations must not trap |
| |
| // IEEE 754 specifies that the following operations |
| // return NaN unless they trap: |
| // 1. sqrt (n); n < 0 |
| // 2. rem (x, 0.0), rem (inf, x) |
| // 3. 0 * inf |
| // 4. 0.0 / 0.0, inf / inf |
| // 5. inf - inf (when both inf have the same sign) |
| |
| // use a variable (not a literal) to prevent warnings |
| volatile FloatT zero = 0; |
| |
| // compute infinity (division may trap) |
| const FloatT inf = FLim::infinity (); |
| |
| FloatT nan = zero * inf; // #3 |
| nan = zero / zero; // #4a |
| nan = inf / inf; // #4b |
| nan = inf - inf; // #5 |
| |
| _RWSTD_UNUSED (inf); |
| _RWSTD_UNUSED (nan); |
| } |
| |
| if (FLim::is_iec559) { |
| // 18.2.1.2, p33 |
| VERIFY_CONST (has_infinity, true); |
| |
| // 18.2.1.2, p36 |
| VERIFY_CONST (has_quiet_NaN, true); |
| |
| // 18.2.1.2, p39 |
| VERIFY_CONST (has_signaling_NaN, true); |
| |
| // 18.2.1.2, p40 - 42 |
| VERIFY_DATA (has_denorm); |
| VERIFY_DATA (has_denorm_loss); |
| } |
| else { |
| // 18.2.1.2, p34 - 42 |
| VERIFY_DATA (has_quiet_NaN); |
| VERIFY_DATA (has_signaling_NaN); |
| VERIFY_DATA (has_denorm); |
| VERIFY_DATA (has_denorm_loss); |
| } |
| } |
| |
| /***********************************************************************/ |
| |
| static int |
| run_test (int, char**) |
| { |
| #undef VERIFY_CONST |
| #define VERIFY_CONST(x, value) do { \ |
| enum { e = x }; \ |
| rw_assert (value == x, 0, __LINE__, \ |
| #x " == %d, got %d", value, x); \ |
| } while (0) |
| |
| // verify values of constants in 18.2.1.3 |
| VERIFY_CONST (std::round_indeterminate, -1); |
| VERIFY_CONST (std::round_toward_zero, 0); |
| VERIFY_CONST (std::round_to_nearest, 1); |
| VERIFY_CONST (std::round_toward_infinity, 2); |
| VERIFY_CONST (std::round_toward_neg_infinity, 3); |
| |
| // verify values of constants in 18.2.1.4 |
| VERIFY_CONST (std::denorm_indeterminate, std::float_denorm_style (-1)); |
| VERIFY_CONST (std::denorm_absent, std::float_denorm_style (0)); |
| VERIFY_CONST (std::denorm_present, std::float_denorm_style (1)); |
| |
| test_limits (float (), "float", "%g"); |
| test_limits (double (), "double", "%g"); |
| |
| #ifndef _RWSTD_NO_LONG_DOUBLE |
| |
| // working around a SunPro 5.3 ICE (PR #25968) |
| # if !defined (__SUNPRO_CC) || __SUNPRO_CC > 0x530 |
| |
| const char fmt[] = "%" _RWSTD_LDBL_PRINTF_PREFIX "g"; |
| test_limits ((long double)0.0, "long double", fmt); |
| |
| # endif // SunPro > 5.3 |
| |
| #endif //_RWSTD_NO_LONG_DOUBLE |
| |
| return 0; |
| } |
| |
| /***********************************************************************/ |
| |
| int main (int argc, char *argv[]) |
| { |
| // working around a bogus EDG eccp warning (see PR #25679) |
| _RWSTD_UNUSED (big_endian); |
| |
| return rw_test (argc, argv, __FILE__, |
| "numeric.special", |
| "floating specializations", |
| run_test, |
| "", |
| (void*)0); |
| } |