From f55ce5fc6f088b49993f520c50548752da3512bb Mon Sep 17 00:00:00 2001 From: Ryan Date: Sun, 10 Sep 2023 10:26:52 -0400 Subject: [PATCH 1/3] initial commit --- doc/fp_utilities/float_next.qbk | 2 +- doc/sf/lambert_w.qbk | 2 +- example/inspect_fp.cpp | 6 ++++-- include/boost/math/ccmath/next.hpp | 2 +- .../boost/math/special_functions/lambert_w.hpp | 3 ++- include/boost/math/special_functions/next.hpp | 15 ++++++++------- test/test_classify.cpp | 3 ++- test/test_difference.cpp | 2 +- test/test_gamma.hpp | 5 +++-- test/test_ibeta_inv.hpp | 3 ++- test/test_lambert_w.cpp | 2 +- test/test_next.cpp | 6 +++--- test/test_next_decimal.cpp | 6 +++--- test/test_students_t.cpp | 3 ++- 14 files changed, 34 insertions(+), 26 deletions(-) diff --git a/doc/fp_utilities/float_next.qbk b/doc/fp_utilities/float_next.qbk index 60d9df9f08..1e299f3882 100644 --- a/doc/fp_utilities/float_next.qbk +++ b/doc/fp_utilities/float_next.qbk @@ -261,7 +261,7 @@ next floating-point value, but `x - u` is not necessarily the previous value. S value. The corner cases occur at power of 2 boundaries. * When the argument becomes very small, it may be that there is no floating-point value that represents one ULP. Whether this is the case or not depends not only on whether the hardware -may ['sometimes] support denormals (as signalled by `std::numeric_limits::has_denorm`), but also whether these are +may ['sometimes] support denormals (as signalled by `boost::math::detail::has_denorm_now()`), but also whether these are currently enabled at runtime (for example on SSE hardware, the DAZ or FTZ flags will disable denormal support). In this situation, the `ulp` function may return a value that is many orders of magnitude too large. diff --git a/doc/sf/lambert_w.qbk b/doc/sf/lambert_w.qbk index 22b13b6ac0..7544a64fed 100644 --- a/doc/sf/lambert_w.qbk +++ b/doc/sf/lambert_w.qbk @@ -289,7 +289,7 @@ The domain of /W/[sub -1] is \[-/e/[super -1], 0\). Numerically, For example, for `double`: lambert_wm1(-2.2250738585072014e-308) = -714.96865723796634 [br] and for `float`: lambert_wm1(-1.17549435e-38) = -91.8567734 [br] -* `z < -std::numeric_limits::min()`, means that z is zero or denormalized (if `std::numeric_limits::has_denorm_min == true`), +* `z < -std::numeric_limits::min()`, means that z is zero or denormalized (if `boost::math::detail::has_denorm_now()_min == true`), for example: `r = lambert_wm1(-std::numeric_limits::denorm_min());` and an overflow_error exception is thrown, and will give a message like: diff --git a/example/inspect_fp.cpp b/example/inspect_fp.cpp index 71ea686f41..5de25f2ac7 100644 --- a/example/inspect_fp.cpp +++ b/example/inspect_fp.cpp @@ -20,6 +20,8 @@ #include #endif +#include // for has_denorm_now + //------------------------------------------------------------------------------ bool is_big_endian() @@ -111,9 +113,9 @@ template void print_table() { print_row("0", (T)0); print_row("sn.min", std::numeric_limits::denorm_min(), - std::numeric_limits::has_denorm); + boost::math::detail::has_denorm_now()); print_row("-sn.min", -std::numeric_limits::denorm_min(), - std::numeric_limits::has_denorm); + boost::math::detail::has_denorm_now()); print_row("n.min/256", (std::numeric_limits::min)()/256); print_row("n.min/2", (std::numeric_limits::min)()/2); print_row("-n.min/2", -(std::numeric_limits::min)()/2); diff --git a/include/boost/math/ccmath/next.hpp b/include/boost/math/ccmath/next.hpp index 30045a0a36..7393815186 100644 --- a/include/boost/math/ccmath/next.hpp +++ b/include/boost/math/ccmath/next.hpp @@ -101,7 +101,7 @@ constexpr T get_smallest_value(const std::false_type&) template constexpr T get_smallest_value() { - return get_smallest_value(std::integral_constant::is_specialized && (std::numeric_limits::has_denorm == std::denorm_present)>()); + return get_smallest_value(std::integral_constant::is_specialized)>()); } template diff --git a/include/boost/math/special_functions/lambert_w.hpp b/include/boost/math/special_functions/lambert_w.hpp index c57ce1d5bd..014282e0cf 100644 --- a/include/boost/math/special_functions/lambert_w.hpp +++ b/include/boost/math/special_functions/lambert_w.hpp @@ -55,6 +55,7 @@ BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES_ITERATIONS // Show evaluation of #include #include // for log (1 + x) #include // For exp_minus_one == 3.67879441171442321595523770161460867e-01. +#include // for has_denorm_now #include // powers with compile time exponent, used in arbitrary precision code. #include // series functor. //#include // polynomial. @@ -1797,7 +1798,7 @@ T lambert_wm1_imp(const T z, const Policy& pol) return -tools::max_value(); } } - if (std::numeric_limits::has_denorm) + if (boost::math::detail::has_denorm_now()) { // All real types except arbitrary precision. if (!(boost::math::isnormal)(z)) { // Almost zero - might also just return infinity like z == 0 or max_value? diff --git a/include/boost/math/special_functions/next.hpp b/include/boost/math/special_functions/next.hpp index 07c55e8d4a..1b56932423 100644 --- a/include/boost/math/special_functions/next.hpp +++ b/include/boost/math/special_functions/next.hpp @@ -82,8 +82,8 @@ inline T normalize_value(const T& val, const std::true_type&) } template -inline T get_smallest_value(std::true_type const&) -{ +inline T get_smallest_value(std::true_type const&) { + static_assert(std::numeric_limits::is_specialized, "Type T must be specialized."); // // numeric_limits lies about denorms being present - particularly // when this can be turned on or off at runtime, as is the case @@ -106,11 +106,12 @@ inline T get_smallest_value(std::false_type const&) template inline T get_smallest_value() { -#if defined(BOOST_MSVC) && (BOOST_MSVC <= 1310) - return get_smallest_value(std::integral_constant::is_specialized && (std::numeric_limits::has_denorm == 1)>()); -#else - return get_smallest_value(std::integral_constant::is_specialized && (std::numeric_limits::has_denorm == std::denorm_present)>()); -#endif + return get_smallest_value(std::integral_constant::is_specialized>()); +} + +template +inline bool has_denorm_now() { + return get_smallest_value() < tools::min_value(); } // diff --git a/test/test_classify.cpp b/test/test_classify.cpp index e6f51a0c77..609a355b04 100644 --- a/test/test_classify.cpp +++ b/test/test_classify.cpp @@ -11,6 +11,7 @@ #include #include #include +#include // for has_denorm_now #define BOOST_TEST_MAIN #include #include @@ -105,7 +106,7 @@ void test_classify(T t, const char* type) } } } - if(std::numeric_limits::has_denorm) + if(boost::math::detail::has_denorm_now()) { t = (std::numeric_limits::min)(); t /= 2; diff --git a/test/test_difference.cpp b/test/test_difference.cpp index b64f2835c4..023947bafa 100644 --- a/test/test_difference.cpp +++ b/test/test_difference.cpp @@ -90,7 +90,7 @@ void test_values(const T& val, const char* name) test_value(-boost::math::tools::epsilon(), name); test_value(boost::math::tools::min_value(), name); test_value(-boost::math::tools::min_value(), name); - if (std::numeric_limits::is_specialized && (std::numeric_limits::has_denorm == std::denorm_present) && ((std::numeric_limits::min)() / 2 != 0)) + if (std::numeric_limits::is_specialized && (boost::math::detail::has_denorm_now() == std::denorm_present) && ((std::numeric_limits::min)() / 2 != 0)) { test_value(z, name); test_value(-z, name); diff --git a/test/test_gamma.hpp b/test/test_gamma.hpp index 5b8c2724e7..c21573dac6 100644 --- a/test/test_gamma.hpp +++ b/test/test_gamma.hpp @@ -11,6 +11,7 @@ #define BOOST_TEST_MAIN #include #include +#include // for has_denorm_now #include #include #include @@ -319,7 +320,7 @@ void test_spots(T, const char* name) BOOST_CHECK(sign == -1); } - if(std::numeric_limits::has_denorm && std::numeric_limits::has_infinity && (boost::math::isinf)(1 / std::numeric_limits::denorm_min())) + if(boost::math::detail::has_denorm_now() && std::numeric_limits::has_infinity && (boost::math::isinf)(1 / std::numeric_limits::denorm_min())) { BOOST_CHECK_EQUAL(boost::math::tgamma(-std::numeric_limits::denorm_min()), -std::numeric_limits::infinity()); BOOST_CHECK_EQUAL(boost::math::tgamma(std::numeric_limits::denorm_min()), std::numeric_limits::infinity()); @@ -336,7 +337,7 @@ void test_spots(T, const char* name) // // Super small values may cause spurious overflow: // - if (std::numeric_limits::is_specialized && std::numeric_limits::has_denorm) + if (std::numeric_limits::is_specialized && boost::math::detail::has_denorm_now()) { T value = (std::numeric_limits::min)(); while (value != 0) diff --git a/test/test_ibeta_inv.hpp b/test/test_ibeta_inv.hpp index 9e303e9235..ba98901773 100644 --- a/test/test_ibeta_inv.hpp +++ b/test/test_ibeta_inv.hpp @@ -8,6 +8,7 @@ #define BOOST_TEST_MAIN #include #include +#include // for has_denorm_now #include #include #include @@ -305,7 +306,7 @@ void test_spots(T) BOOST_MATH_CHECK_THROW(::boost::math::ibeta_inv(static_cast(2.125), -n, static_cast(0.125)), std::domain_error); BOOST_MATH_CHECK_THROW(::boost::math::ibeta_inv(static_cast(2.125), static_cast(1.125), -n), std::domain_error); } - if (std::numeric_limits::has_denorm) + if (boost::math::detail::has_denorm_now()) { T m = std::numeric_limits::denorm_min(); T small = 2 * (std::numeric_limits::min)(); diff --git a/test/test_lambert_w.cpp b/test/test_lambert_w.cpp index 6841f8d6fa..33269e68dd 100644 --- a/test/test_lambert_w.cpp +++ b/test/test_lambert_w.cpp @@ -491,7 +491,7 @@ void test_spots(RealType) } // denorm - but might be == min or zero? - if (std::numeric_limits::has_denorm == true) + if (boost::math::detail::has_denorm_now()) { // Might also return infinity like z == 0? BOOST_CHECK_THROW(lambert_wm1(std::numeric_limits::denorm_min()), std::overflow_error); } diff --git a/test/test_next.cpp b/test/test_next.cpp index 305dd8fee2..bc1179f451 100644 --- a/test/test_next.cpp +++ b/test/test_next.cpp @@ -56,7 +56,7 @@ void test_value(const T& val, const char* name) BOOST_CHECK_EQUAL(float_distance(float_advance(val, 4), val), -4); BOOST_CHECK_EQUAL(float_distance(float_advance(val, -4), val), 4); - if(std::numeric_limits::is_specialized && (std::numeric_limits::has_denorm == std::denorm_present)) + if(std::numeric_limits::is_specialized && (boost::math::detail::has_denorm_now() == std::denorm_present)) { BOOST_CHECK_EQUAL(float_distance(float_advance(float_next(float_next(val)), 4), float_next(float_next(val))), -4); BOOST_CHECK_EQUAL(float_distance(float_advance(float_next(float_next(val)), -4), float_next(float_next(val))), 4); @@ -122,7 +122,7 @@ void test_values(const T& val, const char* name) test_value(-boost::math::tools::epsilon(), name); test_value(boost::math::tools::min_value(), name); test_value(-boost::math::tools::min_value(), name); - if (std::numeric_limits::is_specialized && (std::numeric_limits::has_denorm == std::denorm_present) && ((std::numeric_limits::min)() / 2 != 0)) + if (std::numeric_limits::is_specialized && (boost::math::detail::has_denorm_now() == std::denorm_present) && ((std::numeric_limits::min)() / 2 != 0)) { test_value(z, name); test_value(-z, name); @@ -135,7 +135,7 @@ void test_values(const T& val, const char* name) if((_mm_getcsr() & (_MM_FLUSH_ZERO_ON | 0x40)) == 0) { #endif - if(std::numeric_limits::is_specialized && (std::numeric_limits::has_denorm == std::denorm_present) && ((std::numeric_limits::min)() / 2 != 0)) + if(std::numeric_limits::is_specialized && (boost::math::detail::has_denorm_now() == std::denorm_present) && ((std::numeric_limits::min)() / 2 != 0)) { test_value(std::numeric_limits::denorm_min(), name); test_value(-std::numeric_limits::denorm_min(), name); diff --git a/test/test_next_decimal.cpp b/test/test_next_decimal.cpp index 3d532335bc..786fceffdc 100644 --- a/test/test_next_decimal.cpp +++ b/test/test_next_decimal.cpp @@ -61,7 +61,7 @@ void test_value(const T& val, const char* name) } BOOST_CHECK_EQUAL(float_distance(float_advance(val, 4), val), -4); BOOST_CHECK_EQUAL(float_distance(float_advance(val, -4), val), 4); - if(std::numeric_limits::is_specialized && (std::numeric_limits::has_denorm == std::denorm_present)) + if(std::numeric_limits::is_specialized && (boost::math::detail::has_denorm_now() == std::denorm_present)) { BOOST_CHECK_EQUAL(float_distance(float_advance(float_next(float_next(val)), 4), float_next(float_next(val))), -4); BOOST_CHECK_EQUAL(float_distance(float_advance(float_next(float_next(val)), -4), float_next(float_next(val))), 4); @@ -132,7 +132,7 @@ void test_values(const T& val, const char* name) test_value(T(-boost::math::tools::epsilon()), name); test_value(boost::math::tools::min_value(), name); test_value(T(-boost::math::tools::min_value()), name); - if (std::numeric_limits::is_specialized && (std::numeric_limits::has_denorm == std::denorm_present) && ((std::numeric_limits::min)() / 2 != 0)) + if (std::numeric_limits::is_specialized && (boost::math::detail::has_denorm_now() == std::denorm_present) && ((std::numeric_limits::min)() / 2 != 0)) { test_value(z, name); test_value(T(-z), name); @@ -142,7 +142,7 @@ void test_values(const T& val, const char* name) test_value(radix, name); test_value(T(-radix), name); - if(std::numeric_limits::is_specialized && (std::numeric_limits::has_denorm == std::denorm_present) && ((std::numeric_limits::min)() / 2 != 0)) + if(std::numeric_limits::is_specialized && (boost::math::detail::has_denorm_now() == std::denorm_present) && ((std::numeric_limits::min)() / 2 != 0)) { test_value(std::numeric_limits::denorm_min(), name); test_value(T(-std::numeric_limits::denorm_min()), name); diff --git a/test/test_students_t.cpp b/test/test_students_t.cpp index 59e3fc1860..ad4dc4187b 100644 --- a/test/test_students_t.cpp +++ b/test/test_students_t.cpp @@ -21,6 +21,7 @@ #define BOOST_TEST_MAIN #include // Boost.Test #include +#include // for has_denorm_now #include // for real_concept #include // for real_concept @@ -390,7 +391,7 @@ void test_spots(RealType) // // Bug cases: // - if (std::numeric_limits::is_specialized && std::numeric_limits::has_denorm) + if (std::numeric_limits::is_specialized && boost::math::detail::has_denorm_now()) { BOOST_CHECK_THROW(boost::math::quantile(students_t_distribution((std::numeric_limits::min)() / 2), static_cast(0.0025f)), std::overflow_error); } From 8443719ca94b7bc234561899af7c996f5a5b4b15 Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 11 Sep 2023 14:06:05 -0400 Subject: [PATCH 2/3] remove == std::denorm_present --- doc/sf/lambert_w.qbk | 2 +- test/test_difference.cpp | 2 +- test/test_next.cpp | 6 +++--- test/test_next_decimal.cpp | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/sf/lambert_w.qbk b/doc/sf/lambert_w.qbk index 7544a64fed..27591d9fee 100644 --- a/doc/sf/lambert_w.qbk +++ b/doc/sf/lambert_w.qbk @@ -289,7 +289,7 @@ The domain of /W/[sub -1] is \[-/e/[super -1], 0\). Numerically, For example, for `double`: lambert_wm1(-2.2250738585072014e-308) = -714.96865723796634 [br] and for `float`: lambert_wm1(-1.17549435e-38) = -91.8567734 [br] -* `z < -std::numeric_limits::min()`, means that z is zero or denormalized (if `boost::math::detail::has_denorm_now()_min == true`), +* `z < -std::numeric_limits::min()`, means that z is zero or denormalized (if `boost::math::detail::has_denorm_now() == true`), for example: `r = lambert_wm1(-std::numeric_limits::denorm_min());` and an overflow_error exception is thrown, and will give a message like: diff --git a/test/test_difference.cpp b/test/test_difference.cpp index 023947bafa..7f4ff4f8f2 100644 --- a/test/test_difference.cpp +++ b/test/test_difference.cpp @@ -90,7 +90,7 @@ void test_values(const T& val, const char* name) test_value(-boost::math::tools::epsilon(), name); test_value(boost::math::tools::min_value(), name); test_value(-boost::math::tools::min_value(), name); - if (std::numeric_limits::is_specialized && (boost::math::detail::has_denorm_now() == std::denorm_present) && ((std::numeric_limits::min)() / 2 != 0)) + if (std::numeric_limits::is_specialized && boost::math::detail::has_denorm_now() && ((std::numeric_limits::min)() / 2 != 0)) { test_value(z, name); test_value(-z, name); diff --git a/test/test_next.cpp b/test/test_next.cpp index bc1179f451..b4f05b4379 100644 --- a/test/test_next.cpp +++ b/test/test_next.cpp @@ -56,7 +56,7 @@ void test_value(const T& val, const char* name) BOOST_CHECK_EQUAL(float_distance(float_advance(val, 4), val), -4); BOOST_CHECK_EQUAL(float_distance(float_advance(val, -4), val), 4); - if(std::numeric_limits::is_specialized && (boost::math::detail::has_denorm_now() == std::denorm_present)) + if(std::numeric_limits::is_specialized && boost::math::detail::has_denorm_now()) { BOOST_CHECK_EQUAL(float_distance(float_advance(float_next(float_next(val)), 4), float_next(float_next(val))), -4); BOOST_CHECK_EQUAL(float_distance(float_advance(float_next(float_next(val)), -4), float_next(float_next(val))), 4); @@ -122,7 +122,7 @@ void test_values(const T& val, const char* name) test_value(-boost::math::tools::epsilon(), name); test_value(boost::math::tools::min_value(), name); test_value(-boost::math::tools::min_value(), name); - if (std::numeric_limits::is_specialized && (boost::math::detail::has_denorm_now() == std::denorm_present) && ((std::numeric_limits::min)() / 2 != 0)) + if (std::numeric_limits::is_specialized && boost::math::detail::has_denorm_now() && ((std::numeric_limits::min)() / 2 != 0)) { test_value(z, name); test_value(-z, name); @@ -135,7 +135,7 @@ void test_values(const T& val, const char* name) if((_mm_getcsr() & (_MM_FLUSH_ZERO_ON | 0x40)) == 0) { #endif - if(std::numeric_limits::is_specialized && (boost::math::detail::has_denorm_now() == std::denorm_present) && ((std::numeric_limits::min)() / 2 != 0)) + if(std::numeric_limits::is_specialized && boost::math::detail::has_denorm_now() && ((std::numeric_limits::min)() / 2 != 0)) { test_value(std::numeric_limits::denorm_min(), name); test_value(-std::numeric_limits::denorm_min(), name); diff --git a/test/test_next_decimal.cpp b/test/test_next_decimal.cpp index 786fceffdc..07d59aa877 100644 --- a/test/test_next_decimal.cpp +++ b/test/test_next_decimal.cpp @@ -61,7 +61,7 @@ void test_value(const T& val, const char* name) } BOOST_CHECK_EQUAL(float_distance(float_advance(val, 4), val), -4); BOOST_CHECK_EQUAL(float_distance(float_advance(val, -4), val), 4); - if(std::numeric_limits::is_specialized && (boost::math::detail::has_denorm_now() == std::denorm_present)) + if(std::numeric_limits::is_specialized && boost::math::detail::has_denorm_now()) { BOOST_CHECK_EQUAL(float_distance(float_advance(float_next(float_next(val)), 4), float_next(float_next(val))), -4); BOOST_CHECK_EQUAL(float_distance(float_advance(float_next(float_next(val)), -4), float_next(float_next(val))), 4); @@ -132,7 +132,7 @@ void test_values(const T& val, const char* name) test_value(T(-boost::math::tools::epsilon()), name); test_value(boost::math::tools::min_value(), name); test_value(T(-boost::math::tools::min_value()), name); - if (std::numeric_limits::is_specialized && (boost::math::detail::has_denorm_now() == std::denorm_present) && ((std::numeric_limits::min)() / 2 != 0)) + if (std::numeric_limits::is_specialized && boost::math::detail::has_denorm_now() && ((std::numeric_limits::min)() / 2 != 0)) { test_value(z, name); test_value(T(-z), name); @@ -142,7 +142,7 @@ void test_values(const T& val, const char* name) test_value(radix, name); test_value(T(-radix), name); - if(std::numeric_limits::is_specialized && (boost::math::detail::has_denorm_now() == std::denorm_present) && ((std::numeric_limits::min)() / 2 != 0)) + if(std::numeric_limits::is_specialized && boost::math::detail::has_denorm_now() && ((std::numeric_limits::min)() / 2 != 0)) { test_value(std::numeric_limits::denorm_min(), name); test_value(T(-std::numeric_limits::denorm_min()), name); From ad0a09a5b60bee78fb0108d802584dd12c7239dd Mon Sep 17 00:00:00 2001 From: Ryan Date: Mon, 11 Sep 2023 14:43:49 -0400 Subject: [PATCH 3/3] remove extra ) in ccmath/next --- include/boost/math/ccmath/next.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/boost/math/ccmath/next.hpp b/include/boost/math/ccmath/next.hpp index 7393815186..602c3bc9f9 100644 --- a/include/boost/math/ccmath/next.hpp +++ b/include/boost/math/ccmath/next.hpp @@ -101,7 +101,7 @@ constexpr T get_smallest_value(const std::false_type&) template constexpr T get_smallest_value() { - return get_smallest_value(std::integral_constant::is_specialized)>()); + return get_smallest_value(std::integral_constant::is_specialized>()); } template