diff --git a/include/boost/multiprecision/cpp_bin_float.hpp b/include/boost/multiprecision/cpp_bin_float.hpp index 7678c5e00..82ac4afe7 100644 --- a/include/boost/multiprecision/cpp_bin_float.hpp +++ b/include/boost/multiprecision/cpp_bin_float.hpp @@ -2172,7 +2172,7 @@ class numeric_limits, ExpressionTemplates> infinity() { return boost::multiprecision::cpp_dec_float::inf(); } static constexpr boost::multiprecision::number, ExpressionTemplates> quiet_NaN() { return boost::multiprecision::cpp_dec_float::nan(); } static constexpr boost::multiprecision::number, ExpressionTemplates> signaling_NaN() { return boost::multiprecision::cpp_dec_float::zero(); } - static constexpr boost::multiprecision::number, ExpressionTemplates> denorm_min() { return boost::multiprecision::cpp_dec_float::zero(); } + static constexpr boost::multiprecision::number, ExpressionTemplates> denorm_min() { return (boost::multiprecision::cpp_dec_float::min)(); } }; template diff --git a/include/boost/multiprecision/gmp.hpp b/include/boost/multiprecision/gmp.hpp index fdb848fef..ce02a5dff 100644 --- a/include/boost/multiprecision/gmp.hpp +++ b/include/boost/multiprecision/gmp.hpp @@ -3595,7 +3595,7 @@ class numeric_limits::digits, digit_base_ void test_special_cases() { #if !defined(BOOST_CI_ASAN_BUILD) && !defined(BOOST_CI_USAN_BUID) - test_type max_val = (std::numeric_limits::max)(); - test_type min_val = (std::numeric_limits::min)(); - test_type eps = std::numeric_limits::epsilon(); - test_type inf_val = (std::numeric_limits::infinity)(); - test_type nan_val = (std::numeric_limits::quiet_NaN)(); - test_type half = 0.5; - test_type one_point_5 = 1.5; + test_type max_val = (std::numeric_limits::max)(); + test_type denorm_min_val = std::numeric_limits::denorm_min(); + test_type min_val = (std::numeric_limits::min)(); + test_type eps = std::numeric_limits::epsilon(); + test_type inf_val = (std::numeric_limits::infinity)(); + test_type nan_val = (std::numeric_limits::quiet_NaN)(); + test_type half = 0.5; + test_type one_point_5 = 1.5; BOOST_CHECK((boost::math::isnormal)(max_val)); BOOST_CHECK((boost::math::isnormal)(-max_val)); BOOST_CHECK((boost::math::isnormal)(min_val)); BOOST_CHECK((boost::math::isnormal)(-min_val)); + BOOST_CHECK((boost::math::isnormal)(denorm_min_val)); + BOOST_CHECK((boost::math::isnormal)(-denorm_min_val)); BOOST_CHECK((boost::math::isinf)(inf_val)); BOOST_CHECK((boost::math::isinf)(-inf_val)); BOOST_CHECK((boost::math::isnan)(nan_val)); BOOST_CHECK((boost::math::isnan)(-nan_val)); - if (std::numeric_limits::has_denorm) - min_val = std::numeric_limits::denorm_min(); + if (!std::numeric_limits::has_denorm) { + BOOST_CHECK_EQUAL(denorm_min_val, min_val); + } else { + BOOST_CHECK_LE(denorm_min_val, min_val); + } // Adding epsilon will increment 1.0: BOOST_CHECK(test_type(1) + eps != test_type(1)); @@ -91,6 +97,8 @@ void test_special_cases() BOOST_CHECK_EQUAL(max_val / -half, -inf_val); BOOST_CHECK_EQUAL(max_val / min_val, inf_val); BOOST_CHECK_EQUAL(max_val / -min_val, -inf_val); + BOOST_CHECK_EQUAL(max_val / denorm_min_val, inf_val); + BOOST_CHECK_EQUAL(max_val / -denorm_min_val, -inf_val); // Underflow: BOOST_CHECK_EQUAL(min_val * 2 - one_point_5 * min_val, 0); BOOST_CHECK_EQUAL(-min_val * 2 + one_point_5 * min_val, 0); @@ -98,16 +106,30 @@ void test_special_cases() BOOST_CHECK_EQUAL(min_val / max_val, 0); BOOST_CHECK_EQUAL(min_val * half, 0); BOOST_CHECK_EQUAL(min_val - min_val, 0); - BOOST_CHECK_EQUAL(max_val - max_val, 0); BOOST_CHECK_EQUAL(-min_val + min_val, 0); - BOOST_CHECK_EQUAL(-max_val + max_val, 0); + + BOOST_CHECK_EQUAL(denorm_min_val * 2 - one_point_5 * denorm_min_val, 0); + BOOST_CHECK_EQUAL(-denorm_min_val * 2 + one_point_5 * denorm_min_val, 0); + BOOST_CHECK_EQUAL(denorm_min_val / 2, 0); + BOOST_CHECK_EQUAL(denorm_min_val / max_val, 0); + BOOST_CHECK_EQUAL(denorm_min_val * half, 0); + BOOST_CHECK_EQUAL(denorm_min_val - denorm_min_val, 0); + BOOST_CHECK_EQUAL(-denorm_min_val + denorm_min_val, 0); // Things which should not over/underflow: BOOST_CHECK_EQUAL((min_val * 2) / 2, min_val); - BOOST_CHECK_EQUAL((max_val / 2) * 2, max_val); BOOST_CHECK_GE((min_val * 2.0000001) / 1.9999999999999999, min_val); - BOOST_CHECK_LE((max_val / 2.0000001) * 1.9999999999999999, max_val); BOOST_CHECK_EQUAL(min_val * 2 - min_val, min_val); + + BOOST_CHECK_EQUAL((denorm_min_val * 2) / 2, denorm_min_val); + BOOST_CHECK_GE((denorm_min_val * 2.0000001) / 1.9999999999999999, denorm_min_val); + BOOST_CHECK_EQUAL(denorm_min_val * 2 - denorm_min_val, denorm_min_val); + + BOOST_CHECK_EQUAL(-max_val + max_val, 0); + BOOST_CHECK_EQUAL(max_val - max_val, 0); + BOOST_CHECK_EQUAL((max_val / 2) * 2, max_val); + BOOST_CHECK_LE((max_val / 2.0000001) * 1.9999999999999999, max_val); BOOST_CHECK_EQUAL(max_val / 2 + max_val / 2, max_val); + // Things involving zero: BOOST_CHECK_EQUAL(max_val + 0, max_val); BOOST_CHECK_EQUAL(max_val - 0, max_val); @@ -210,12 +232,20 @@ void test_special_cases() // Corner cases: BOOST_CHECK_EQUAL((max_val * half) / half, max_val); BOOST_CHECK_EQUAL((max_val / 2) * 2, max_val); + BOOST_CHECK_EQUAL((min_val / half) * half, min_val); BOOST_CHECK_EQUAL((min_val * 2) / 2, min_val); BOOST_CHECK_EQUAL(max_val + min_val, max_val); BOOST_CHECK_EQUAL(min_val + max_val, max_val); BOOST_CHECK_EQUAL(max_val - min_val, max_val); BOOST_CHECK_EQUAL(min_val - max_val, -max_val); + + BOOST_CHECK_EQUAL((denorm_min_val / half) * half, denorm_min_val); + BOOST_CHECK_EQUAL((denorm_min_val * 2) / 2, denorm_min_val); + BOOST_CHECK_EQUAL(max_val + denorm_min_val, max_val); + BOOST_CHECK_EQUAL(denorm_min_val + max_val, max_val); + BOOST_CHECK_EQUAL(max_val - denorm_min_val, max_val); + BOOST_CHECK_EQUAL(denorm_min_val - max_val, -max_val); // Signed zeros: BOOST_CHECK(boost::math::signbit(min_val * -min_val)); BOOST_CHECK(boost::math::signbit(min_val * min_val) == 0); @@ -230,6 +260,24 @@ void test_special_cases() BOOST_CHECK(boost::math::signbit(-min_val / -2) == 0); BOOST_CHECK(boost::math::signbit(-min_val / 2)); test_type neg_zero = min_val * -min_val; + + BOOST_CHECK(boost::math::signbit(denorm_min_val * -denorm_min_val)); + BOOST_CHECK(boost::math::signbit(denorm_min_val * denorm_min_val) == 0); + BOOST_CHECK(boost::math::signbit(-denorm_min_val * -denorm_min_val) == 0); + BOOST_CHECK(boost::math::signbit(-denorm_min_val * denorm_min_val)); + BOOST_CHECK(boost::math::signbit(denorm_min_val / max_val) == 0); + BOOST_CHECK(boost::math::signbit(denorm_min_val / -max_val)); + BOOST_CHECK(boost::math::signbit(-denorm_min_val / -max_val) == 0); + BOOST_CHECK(boost::math::signbit(-denorm_min_val / max_val)); + BOOST_CHECK(boost::math::signbit(denorm_min_val / 2) == 0); + BOOST_CHECK(boost::math::signbit(denorm_min_val / -2)); + BOOST_CHECK(boost::math::signbit(-denorm_min_val / -2) == 0); + BOOST_CHECK(boost::math::signbit(-denorm_min_val / 2)); + + test_type neg_denorm_zero = denorm_min_val * -denorm_min_val; + + BOOST_CHECK_EQUAL(neg_zero, neg_denorm_zero); + test_type zero = 0; // Arithmetic involving signed zero: BOOST_CHECK_EQUAL(-neg_zero, 0); diff --git a/test/test_numeric_limits.cpp b/test/test_numeric_limits.cpp index fe8885af4..685be8ddd 100644 --- a/test/test_numeric_limits.cpp +++ b/test/test_numeric_limits.cpp @@ -83,9 +83,9 @@ void test_specific(const std::integral_constant::is_specialized) + BOOST_IF_CONSTEXPR(std::numeric_limits::is_specialized) { - if (std::numeric_limits::has_quiet_NaN) + BOOST_IF_CONSTEXPR(std::numeric_limits::has_quiet_NaN) { BOOST_TEST((boost::math::isnan)(std::numeric_limits::quiet_NaN())); BOOST_TEST(FP_NAN == (boost::math::fpclassify)(std::numeric_limits::quiet_NaN())); @@ -93,7 +93,7 @@ void test_specific(const std::integral_constant::quiet_NaN())); BOOST_TEST(!(boost::math::isinf)(std::numeric_limits::quiet_NaN())); } - if (std::numeric_limits::has_signaling_NaN) + BOOST_IF_CONSTEXPR(std::numeric_limits::has_signaling_NaN) { BOOST_TEST((boost::math::isnan)(std::numeric_limits::signaling_NaN())); BOOST_TEST(FP_NAN == (boost::math::fpclassify)(std::numeric_limits::signaling_NaN())); @@ -101,7 +101,7 @@ void test_specific(const std::integral_constant::signaling_NaN())); BOOST_TEST(!(boost::math::isinf)(std::numeric_limits::signaling_NaN())); } - if (std::numeric_limits::has_infinity) + BOOST_IF_CONSTEXPR(std::numeric_limits::has_infinity) { BOOST_TEST((boost::math::isinf)(std::numeric_limits::infinity())); BOOST_TEST(FP_INFINITE == (boost::math::fpclassify)(std::numeric_limits::infinity())); @@ -109,7 +109,7 @@ void test_specific(const std::integral_constant::infinity())); BOOST_TEST(!(boost::math::isnan)(std::numeric_limits::infinity())); } - if (std::numeric_limits::has_denorm == std::denorm_present) + BOOST_IF_CONSTEXPR(std::numeric_limits::has_denorm == std::denorm_present) { BOOST_TEST(FP_SUBNORMAL == (boost::math::fpclassify)(std::numeric_limits::denorm_min())); BOOST_TEST(FP_SUBNORMAL == (boost::math::fpclassify)((std::numeric_limits::min)() / 2)); @@ -121,6 +121,16 @@ void test_specific(const std::integral_constant::min)() / 2); BOOST_TEST(0 != std::numeric_limits::denorm_min()); } + BOOST_IF_CONSTEXPR(std::numeric_limits::has_denorm == std::denorm_absent) + { + BOOST_TEST(std::numeric_limits::denorm_min() > 0); + BOOST_TEST(!(std::numeric_limits::denorm_min() > (std::numeric_limits::min)())); + BOOST_TEST(!(std::numeric_limits::denorm_min() < (std::numeric_limits::min)())); + BOOST_TEST(std::numeric_limits::denorm_min() == (std::numeric_limits::min)()); + BOOST_TEST(FP_NORMAL == (boost::math::fpclassify)(std::numeric_limits::denorm_min())); + BOOST_TEST(FP_NORMAL == (boost::math::fpclassify)(-std::numeric_limits::denorm_min())); + BOOST_TEST(FP_ZERO != (boost::math::fpclassify)(std::numeric_limits::denorm_min())); + } } Number n = 0; BOOST_TEST((boost::math::fpclassify)(n) == FP_ZERO); @@ -135,11 +145,11 @@ void test_specific(const std::integral_constant::round_style == std::round_to_nearest) + BOOST_IF_CONSTEXPR(std::numeric_limits::round_style == std::round_to_nearest) { BOOST_CHECK_EQUAL(std::numeric_limits::round_error(), 0.5); } - else if (std::numeric_limits::round_style != std::round_indeterminate) + else BOOST_IF_CONSTEXPR(std::numeric_limits::round_style != std::round_indeterminate) { // Round error is 1.0: BOOST_CHECK_EQUAL(std::numeric_limits::round_error(), 1); @@ -154,9 +164,9 @@ void test_specific(const std::integral_constant void test_specific(const std::integral_constant&) { - if (std::numeric_limits::is_modulo) + BOOST_IF_CONSTEXPR(std::numeric_limits::is_modulo) { - if (!std::numeric_limits::is_signed) + BOOST_IF_CONSTEXPR(!std::numeric_limits::is_signed) { BOOST_TEST(1 + (std::numeric_limits::max)() == 0); BOOST_TEST(--Number(0) == (std::numeric_limits::max)()); @@ -186,13 +196,13 @@ void test() std::cout << "numeric_limits values for type " << typeid(Number).name() << std::endl; PRINT(is_specialized); - if (std::numeric_limits::is_integer) + BOOST_IF_CONSTEXPR(std::numeric_limits::is_integer) { std::cout << std::hex << std::showbase; } std::cout << "max()" << " = " << (std::numeric_limits::max)() << std::endl; - if (std::numeric_limits::is_integer) + BOOST_IF_CONSTEXPR(std::numeric_limits::is_integer) { std::cout << std::dec; }