Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

has_denorm_now #1029

Merged
merged 3 commits into from
Oct 30, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion doc/fp_utilities/float_next.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
2 changes: 1 addition & 1 deletion doc/sf/lambert_w.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
6 changes: 4 additions & 2 deletions example/inspect_fp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion include/boost/math/ccmath/next.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
3 changes: 2 additions & 1 deletion include/boost/math/special_functions/lambert_w.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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?
Expand Down
15 changes: 8 additions & 7 deletions include/boost/math/special_functions/next.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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>();
}

//
Expand Down
3 changes: 2 additions & 1 deletion test/test_classify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion test/test_difference.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
5 changes: 3 additions & 2 deletions test/test_gamma.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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());
Expand All @@ -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)
Expand Down
3 changes: 2 additions & 1 deletion test/test_ibeta_inv.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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)();
Expand Down
2 changes: 1 addition & 1 deletion test/test_lambert_w.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
6 changes: 3 additions & 3 deletions test/test_next.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions test/test_next_decimal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion test/test_students_t.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
}
Expand Down