has_denorm_now (#1029)

* initial commit

* remove == std::denorm_present

* remove extra ) in ccmath/next
This commit is contained in:
ryanelandt 2023-10-30 04:37:29 -04:00 committed by GitHub
parent 14f4e3a21f
commit 2c74b149b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 34 additions and 26 deletions

View File

@ -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<FPT>::has_denorm`), but also whether these are
may ['sometimes] support denormals (as signalled by `boost::math::detail::has_denorm_now<FPT>()`), 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.

View File

@ -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<T>::min()`, means that z is zero or denormalized (if `std::numeric_limits<T>::has_denorm_min == true`),
* `z < -std::numeric_limits<T>::min()`, means that z is zero or denormalized (if `boost::math::detail::has_denorm_now<T>() == true`),
for example: `r = lambert_wm1(-std::numeric_limits<double>::denorm_min());` and an overflow_error exception is thrown,
and will give a message like:

View File

@ -20,6 +20,8 @@
#include <boost/math/tools/config.hpp>
#endif
#include <boost/math/special_functions/next.hpp> // for has_denorm_now
//------------------------------------------------------------------------------
bool is_big_endian()
@ -111,9 +113,9 @@ template<class T> void print_table()
{
print_row("0", (T)0);
print_row("sn.min", std::numeric_limits<T>::denorm_min(),
std::numeric_limits<T>::has_denorm);
boost::math::detail::has_denorm_now<T>());
print_row("-sn.min", -std::numeric_limits<T>::denorm_min(),
std::numeric_limits<T>::has_denorm);
boost::math::detail::has_denorm_now<T>());
print_row("n.min/256", (std::numeric_limits<T>::min)()/256);
print_row("n.min/2", (std::numeric_limits<T>::min)()/2);
print_row("-n.min/2", -(std::numeric_limits<T>::min)()/2);

View File

@ -101,7 +101,7 @@ constexpr T get_smallest_value(const std::false_type&)
template <typename T>
constexpr T get_smallest_value()
{
return get_smallest_value<T>(std::integral_constant<bool, std::numeric_limits<T>::is_specialized && (std::numeric_limits<T>::has_denorm == std::denorm_present)>());
return get_smallest_value<T>(std::integral_constant<bool, std::numeric_limits<T>::is_specialized>());
}
template <typename T>

View File

@ -55,6 +55,7 @@ BOOST_MATH_INSTRUMENT_LAMBERT_W_SMALL_Z_SERIES_ITERATIONS // Show evaluation of
#include <boost/math/special_functions/fpclassify.hpp>
#include <boost/math/special_functions/log1p.hpp> // for log (1 + x)
#include <boost/math/constants/constants.hpp> // For exp_minus_one == 3.67879441171442321595523770161460867e-01.
#include <boost/math/special_functions/next.hpp> // for has_denorm_now
#include <boost/math/special_functions/pow.hpp> // powers with compile time exponent, used in arbitrary precision code.
#include <boost/math/tools/series.hpp> // series functor.
//#include <boost/math/tools/polynomial.hpp> // polynomial.
@ -1797,7 +1798,7 @@ T lambert_wm1_imp(const T z, const Policy& pol)
return -tools::max_value<T>();
}
}
if (std::numeric_limits<T>::has_denorm)
if (boost::math::detail::has_denorm_now<T>())
{ // All real types except arbitrary precision.
if (!(boost::math::isnormal)(z))
{ // Almost zero - might also just return infinity like z == 0 or max_value?

View File

@ -82,8 +82,8 @@ inline T normalize_value(const T& val, const std::true_type&)
}
template <class T>
inline T get_smallest_value(std::true_type const&)
{
inline T get_smallest_value(std::true_type const&) {
static_assert(std::numeric_limits<T>::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 <class T>
inline T get_smallest_value()
{
#if defined(BOOST_MSVC) && (BOOST_MSVC <= 1310)
return get_smallest_value<T>(std::integral_constant<bool, std::numeric_limits<T>::is_specialized && (std::numeric_limits<T>::has_denorm == 1)>());
#else
return get_smallest_value<T>(std::integral_constant<bool, std::numeric_limits<T>::is_specialized && (std::numeric_limits<T>::has_denorm == std::denorm_present)>());
#endif
return get_smallest_value<T>(std::integral_constant<bool, std::numeric_limits<T>::is_specialized>());
}
template <class T>
inline bool has_denorm_now() {
return get_smallest_value<T>() < tools::min_value<T>();
}
//

View File

@ -11,6 +11,7 @@
#include <boost/limits.hpp>
#include <boost/math/concepts/real_concept.hpp>
#include <boost/math/special_functions/fpclassify.hpp>
#include <boost/math/special_functions/next.hpp> // for has_denorm_now
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <iostream>
@ -105,7 +106,7 @@ void test_classify(T t, const char* type)
}
}
}
if(std::numeric_limits<T>::has_denorm)
if(boost::math::detail::has_denorm_now<T>())
{
t = (std::numeric_limits<T>::min)();
t /= 2;

View File

@ -90,7 +90,7 @@ void test_values(const T& val, const char* name)
test_value(-boost::math::tools::epsilon<T>(), name);
test_value(boost::math::tools::min_value<T>(), name);
test_value(-boost::math::tools::min_value<T>(), name);
if (std::numeric_limits<T>::is_specialized && (std::numeric_limits<T>::has_denorm == std::denorm_present) && ((std::numeric_limits<T>::min)() / 2 != 0))
if (std::numeric_limits<T>::is_specialized && boost::math::detail::has_denorm_now<T>() && ((std::numeric_limits<T>::min)() / 2 != 0))
{
test_value(z, name);
test_value(-z, name);

View File

@ -11,6 +11,7 @@
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <boost/test/tools/floating_point_comparison.hpp>
#include <boost/math/special_functions/next.hpp> // for has_denorm_now
#include <boost/math/tools/stats.hpp>
#include <boost/math/tools/test.hpp>
#include <boost/math/constants/constants.hpp>
@ -319,7 +320,7 @@ void test_spots(T, const char* name)
BOOST_CHECK(sign == -1);
}
if(std::numeric_limits<T>::has_denorm && std::numeric_limits<T>::has_infinity && (boost::math::isinf)(1 / std::numeric_limits<T>::denorm_min()))
if(boost::math::detail::has_denorm_now<T>() && std::numeric_limits<T>::has_infinity && (boost::math::isinf)(1 / std::numeric_limits<T>::denorm_min()))
{
BOOST_CHECK_EQUAL(boost::math::tgamma(-std::numeric_limits<T>::denorm_min()), -std::numeric_limits<T>::infinity());
BOOST_CHECK_EQUAL(boost::math::tgamma(std::numeric_limits<T>::denorm_min()), std::numeric_limits<T>::infinity());
@ -336,7 +337,7 @@ void test_spots(T, const char* name)
//
// Super small values may cause spurious overflow:
//
if (std::numeric_limits<T>::is_specialized && std::numeric_limits<T>::has_denorm)
if (std::numeric_limits<T>::is_specialized && boost::math::detail::has_denorm_now<T>())
{
T value = (std::numeric_limits<T>::min)();
while (value != 0)

View File

@ -8,6 +8,7 @@
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp>
#include <boost/test/tools/floating_point_comparison.hpp>
#include <boost/math/special_functions/next.hpp> // for has_denorm_now
#include <boost/math/special_functions/math_fwd.hpp>
#include <boost/math/tools/stats.hpp>
#include <boost/math/tools/test.hpp>
@ -305,7 +306,7 @@ void test_spots(T)
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_inv(static_cast<T>(2.125), -n, static_cast<T>(0.125)), std::domain_error);
BOOST_MATH_CHECK_THROW(::boost::math::ibeta_inv(static_cast<T>(2.125), static_cast<T>(1.125), -n), std::domain_error);
}
if (std::numeric_limits<T>::has_denorm)
if (boost::math::detail::has_denorm_now<T>())
{
T m = std::numeric_limits<T>::denorm_min();
T small = 2 * (std::numeric_limits<T>::min)();

View File

@ -491,7 +491,7 @@ void test_spots(RealType)
}
// denorm - but might be == min or zero?
if (std::numeric_limits<RealType>::has_denorm == true)
if (boost::math::detail::has_denorm_now<RealType>())
{ // Might also return infinity like z == 0?
BOOST_CHECK_THROW(lambert_wm1(std::numeric_limits<RealType>::denorm_min()), std::overflow_error);
}

View File

@ -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<T>::is_specialized && (std::numeric_limits<T>::has_denorm == std::denorm_present))
if(std::numeric_limits<T>::is_specialized && boost::math::detail::has_denorm_now<T>())
{
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<T>(), name);
test_value(boost::math::tools::min_value<T>(), name);
test_value(-boost::math::tools::min_value<T>(), name);
if (std::numeric_limits<T>::is_specialized && (std::numeric_limits<T>::has_denorm == std::denorm_present) && ((std::numeric_limits<T>::min)() / 2 != 0))
if (std::numeric_limits<T>::is_specialized && boost::math::detail::has_denorm_now<T>() && ((std::numeric_limits<T>::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<T>::is_specialized && (std::numeric_limits<T>::has_denorm == std::denorm_present) && ((std::numeric_limits<T>::min)() / 2 != 0))
if(std::numeric_limits<T>::is_specialized && boost::math::detail::has_denorm_now<T>() && ((std::numeric_limits<T>::min)() / 2 != 0))
{
test_value(std::numeric_limits<T>::denorm_min(), name);
test_value(-std::numeric_limits<T>::denorm_min(), name);

View File

@ -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<T>::is_specialized && (std::numeric_limits<T>::has_denorm == std::denorm_present))
if(std::numeric_limits<T>::is_specialized && boost::math::detail::has_denorm_now<T>())
{
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<T>()), name);
test_value(boost::math::tools::min_value<T>(), name);
test_value(T(-boost::math::tools::min_value<T>()), name);
if (std::numeric_limits<T>::is_specialized && (std::numeric_limits<T>::has_denorm == std::denorm_present) && ((std::numeric_limits<T>::min)() / 2 != 0))
if (std::numeric_limits<T>::is_specialized && boost::math::detail::has_denorm_now<T>() && ((std::numeric_limits<T>::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<T>::is_specialized && (std::numeric_limits<T>::has_denorm == std::denorm_present) && ((std::numeric_limits<T>::min)() / 2 != 0))
if(std::numeric_limits<T>::is_specialized && boost::math::detail::has_denorm_now<T>() && ((std::numeric_limits<T>::min)() / 2 != 0))
{
test_value(std::numeric_limits<T>::denorm_min(), name);
test_value(T(-std::numeric_limits<T>::denorm_min()), name);

View File

@ -21,6 +21,7 @@
#define BOOST_TEST_MAIN
#include <boost/test/unit_test.hpp> // Boost.Test
#include <boost/test/tools/floating_point_comparison.hpp>
#include <boost/math/special_functions/next.hpp> // for has_denorm_now
#include <boost/math/concepts/real_concept.hpp> // for real_concept
#include <boost/math/tools/test.hpp> // for real_concept
@ -390,7 +391,7 @@ void test_spots(RealType)
//
// Bug cases:
//
if (std::numeric_limits<RealType>::is_specialized && std::numeric_limits<RealType>::has_denorm)
if (std::numeric_limits<RealType>::is_specialized && boost::math::detail::has_denorm_now<RealType>())
{
BOOST_CHECK_THROW(boost::math::quantile(students_t_distribution<RealType>((std::numeric_limits<RealType>::min)() / 2), static_cast<RealType>(0.0025f)), std::overflow_error);
}