diff --git a/include/boost/multiprecision/cpp_bin_float.hpp b/include/boost/multiprecision/cpp_bin_float.hpp index 4d873b4be..3745b2c53 100644 --- a/include/boost/multiprecision/cpp_bin_float.hpp +++ b/include/boost/multiprecision/cpp_bin_float.hpp @@ -346,7 +346,7 @@ class cpp_bin_float m_sign = false; m_exponent = 0; - constexpr std::ptrdiff_t bits = sizeof(int) * CHAR_BIT - 1 < MaxExponent - 1 ? sizeof(int) * CHAR_BIT - 1 : 3; + constexpr std::ptrdiff_t bits = static_cast(sizeof(int) * CHAR_BIT - 1) < MaxExponent - 1 ? sizeof(int) * CHAR_BIT - 1 : 3; int e; f = frexpq(f, &e); while (f) diff --git a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_limits.hpp b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_limits.hpp index 7d8b01c1f..6070372e0 100644 --- a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_limits.hpp +++ b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_limits.hpp @@ -58,6 +58,7 @@ struct numeric_limits::max_exponent10; static constexpr int min_exponent10 = std::numeric_limits::min_exponent10; + // LCOV_EXCL_START static constexpr auto (min) () noexcept -> float_type { return (std::numeric_limits::min) (); } static constexpr auto (max) () noexcept -> float_type { return (std::numeric_limits::max) (); } static constexpr auto lowest () noexcept -> float_type { return std::numeric_limits::lowest (); } @@ -67,6 +68,7 @@ struct numeric_limits float_type { return std::numeric_limits::infinity (); } static constexpr auto quiet_NaN () noexcept -> float_type { return std::numeric_limits::quiet_NaN (); } static constexpr auto signaling_NaN() noexcept -> float_type { return std::numeric_limits::signaling_NaN(); } + // LCOV_EXCL_STOP }; #if defined(BOOST_MP_CPP_DOUBLE_FP_HAS_FLOAT128) diff --git a/include/boost/multiprecision/cpp_double_fp.hpp b/include/boost/multiprecision/cpp_double_fp.hpp index 704953050..e2f2bb21a 100644 --- a/include/boost/multiprecision/cpp_double_fp.hpp +++ b/include/boost/multiprecision/cpp_double_fp.hpp @@ -89,6 +89,10 @@ constexpr int eval_fpclassify(const cpp_double_fp_backend& o) template constexpr void eval_sqrt(cpp_double_fp_backend& result, const cpp_double_fp_backend& o); +template +constexpr typename ::std::enable_if<::std::is_integral::value, void>::type eval_pow(cpp_double_fp_backend& result, const cpp_double_fp_backend& x, IntegralType n); + template ::value && ((cpp_df_qf_detail::ccmath::numeric_limits::digits10 * 2) < 16))>::type const* = nullptr> constexpr void eval_exp(cpp_double_fp_backend& result, const cpp_double_fp_backend& x); @@ -586,79 +590,7 @@ class cpp_double_fp_backend } } - // The multiplication algorithm has been taken from Victor Shoup, - // package WinNTL-5_3_2. It might originally be related to the - // K. Briggs work. The algorithm has been significantly simplified - // while still atempting to retain proper rounding corrections. - - float_type C { cpp_df_qf_detail::split_maker::value * data.first }; - - float_type hu { }; - - if (cpp_df_qf_detail::ccmath::isinf(C)) - { - // Handle overflow by scaling down (and then back up) with the split. - - C = data.first - cpp_df_qf_detail::ccmath::ldexp(data.first, -cpp_df_qf_detail::split_maker::n_shl); - - hu = cpp_df_qf_detail::ccmath::ldexp(data.first - C, cpp_df_qf_detail::split_maker::n_shl); - } - else - { - hu = C - float_type { C - data.first }; - } - - C = data.first * v.data.first; - - if (cpp_df_qf_detail::ccmath::isinf(C)) - { - // Handle overflow. - const bool b_neg { (isneg_unchecked() != v.isneg_unchecked()) }; - - *this = cpp_double_fp_backend::my_value_inf(); - - if (b_neg) - { - negate(); - } - - return *this; - } - - float_type c { cpp_df_qf_detail::split_maker::value * v.data.first }; - - float_type hv { }; - - if (cpp_df_qf_detail::ccmath::isinf(c)) - { - // Handle overflow by scaling down (and then back up) with the split. - - c = v.data.first - cpp_df_qf_detail::ccmath::ldexp(v.data.first, -cpp_df_qf_detail::split_maker::n_shl); - - hv = cpp_df_qf_detail::ccmath::ldexp(v.data.first - c, cpp_df_qf_detail::split_maker::n_shl); - } - else - { - hv = c - float_type { c - v.data.first }; - } - - const float_type tv { v.data.first - hv }; - - float_type t1 { cpp_df_qf_detail::ccmath::unsafe::fma(hu, hv, -C) }; - - t1 = cpp_df_qf_detail::ccmath::unsafe::fma(hu, tv, t1); - - const float_type tu { data.first - hu }; - - t1 = cpp_df_qf_detail::ccmath::unsafe::fma(tu, hv, t1); - - c = cpp_df_qf_detail::ccmath::unsafe::fma(tu, tv, t1) - + (data.first * v.data.second) - + (data.second * v.data.first); - - // Perform even more simplifications compared to Victor Shoup. - data.first = C + c; - data.second = float_type { C - data.first } + c; + mul_unchecked(v); return *this; } @@ -813,35 +745,74 @@ class cpp_double_fp_backend return *this; } - constexpr cpp_double_fp_backend operator++(int) + // Unare minus operator. + constexpr cpp_double_fp_backend operator-() const { - cpp_double_fp_backend t(*this); + cpp_double_fp_backend v { *this }; - ++(*this); + v.negate(); - return t; + return v; } - constexpr cpp_double_fp_backend operator--(int) + // Helper functions. + constexpr static void pown(cpp_double_fp_backend& result, const cpp_double_fp_backend& x, int p) { - cpp_double_fp_backend t(*this); + using local_float_type = cpp_double_fp_backend; - --(*this); + if (p == 2) + { + result = x; result.mul_unchecked(x); + } + else if (p == 3) + { + result = x; result.mul_unchecked(x); result.mul_unchecked(x); + } + else if (p == 4) + { + local_float_type x2 { x }; + x2.mul_unchecked(x); - return t; - } + result = x2; + result.mul_unchecked(x2); + } + else if (p == 1) + { + result = x; + } + else if (p < 0) + { + // The case p == 0 is checked in a higher calling layer. - constexpr cpp_double_fp_backend& operator++() { return *this += cpp_double_fp_backend(float_type(1.0F)); } - constexpr cpp_double_fp_backend& operator--() { return *this -= cpp_double_fp_backend(float_type(1.0F)); } + pown(result, local_float_type(1U) / x, -p); + } + else + { + result = local_float_type(1U); - // Unare minus operator. - constexpr cpp_double_fp_backend operator-() const - { - cpp_double_fp_backend v(*this); + local_float_type y(x); - v.negate(); + auto p_local = static_cast(p); - return v; + for (;;) + { + if (static_cast(p_local & static_cast(UINT8_C(1))) != static_cast(UINT8_C(0))) + { + result.mul_unchecked(y); + } + + p_local >>= 1U; + + if (p_local == static_cast(UINT8_C(0))) + { + break; + } + else + { + y.mul_unchecked(y); + } + } + } } constexpr void swap(cpp_double_fp_backend& other) @@ -1004,6 +975,83 @@ class cpp_double_fp_backend bool rd_string(const char* pstr); + constexpr void mul_unchecked(const cpp_double_fp_backend& v) + { + // The multiplication algorithm has been taken from Victor Shoup, + // package WinNTL-5_3_2. It might originally be related to the + // K. Briggs work. The algorithm has been significantly simplified + // while still atempting to retain proper rounding corrections. + + float_type C { cpp_df_qf_detail::split_maker::value * data.first }; + + float_type hu { }; + + if (cpp_df_qf_detail::ccmath::isinf(C)) + { + // Handle overflow by scaling down (and then back up) with the split. + + C = data.first - cpp_df_qf_detail::ccmath::ldexp(data.first, -cpp_df_qf_detail::split_maker::n_shl); + + hu = cpp_df_qf_detail::ccmath::ldexp(data.first - C, cpp_df_qf_detail::split_maker::n_shl); + } + else + { + hu = C - float_type { C - data.first }; + } + + C = data.first * v.data.first; + + if (cpp_df_qf_detail::ccmath::isinf(C)) + { + // Handle overflow. + const bool b_neg { (isneg_unchecked() != v.isneg_unchecked()) }; + + *this = cpp_double_fp_backend::my_value_inf(); + + if (b_neg) + { + negate(); + } + + return; + } + + float_type c { cpp_df_qf_detail::split_maker::value * v.data.first }; + + float_type hv { }; + + if (cpp_df_qf_detail::ccmath::isinf(c)) + { + // Handle overflow by scaling down (and then back up) with the split. + + c = v.data.first - cpp_df_qf_detail::ccmath::ldexp(v.data.first, -cpp_df_qf_detail::split_maker::n_shl); + + hv = cpp_df_qf_detail::ccmath::ldexp(v.data.first - c, cpp_df_qf_detail::split_maker::n_shl); + } + else + { + hv = c - float_type { c - v.data.first }; + } + + const float_type tv { v.data.first - hv }; + + float_type t1 { cpp_df_qf_detail::ccmath::unsafe::fma(hu, hv, -C) }; + + t1 = cpp_df_qf_detail::ccmath::unsafe::fma(hu, tv, t1); + + const float_type tu { data.first - hu }; + + t1 = cpp_df_qf_detail::ccmath::unsafe::fma(tu, hv, t1); + + c = cpp_df_qf_detail::ccmath::unsafe::fma(tu, tv, t1) + + (data.first * v.data.second) + + (data.second * v.data.first); + + // Perform even more simplifications compared to Victor Shoup. + data.first = C + c; + data.second = float_type { C - data.first } + c; + } + template ::value && ((cpp_df_qf_detail::ccmath::numeric_limits::digits10 * 2) < 16))>::type const*> friend constexpr void eval_exp(cpp_double_fp_backend& result, const cpp_double_fp_backend& x); @@ -1394,6 +1442,76 @@ constexpr void eval_sqrt(cpp_double_fp_backend& result, const result.rep().second = local_float_type { c - result.rep().first } + cc; } +template +constexpr typename ::std::enable_if<::std::is_integral::value, void>::type eval_pow(cpp_double_fp_backend& result, const cpp_double_fp_backend& x, IntegralType p) +{ + const int fpc { eval_fpclassify(x) }; + + using double_float_type = cpp_double_fp_backend; + + using local_integral_type = IntegralType; + + const bool p_is_odd { (static_cast(p & 1) != static_cast(0)) }; + + if (p == static_cast(0)) + { + // pow(base, +/-0) returns 1 for any base, even when base is NaN. + + result = double_float_type { unsigned { UINT8_C(1) } }; + } + else if (fpc != FP_NORMAL) + { + if (fpc == FP_ZERO) + { + // pow( +0, exp), where exp is a negative odd integer, returns +infinity. + // pow( -0, exp), where exp is a negative odd integer, returns +infinity. + // pow(+/-0, exp), where exp is a negative even integer, returns +infinity. + + // pow( +0, exp), where exp is a positive odd integer, returns +0. + // pow( -0, exp), where exp is a positive odd integer, returns -0. + // pow(+/-0, exp), where exp is a positive even integer, returns +0. + + result = ((p < static_cast(0)) ? double_float_type::my_value_inf() : double_float_type { unsigned { UINT8_C(0) } }); + } + else if (fpc == FP_INFINITE) + { + if (eval_signbit(x)) + { + if (p < static_cast(0)) + { + // pow(-infinity, exp) returns -0 if exp is a negative odd integer. + // pow(-infinity, exp) returns +0 if exp is a negative even integer. + + result = double_float_type { unsigned { UINT8_C(0) } }; + } + else + { + // pow(-infinity, exp) returns -infinity if exp is a positive odd integer. + // pow(-infinity, exp) returns +infinity if exp is a positive even integer. + + result = (p_is_odd ? -double_float_type::my_value_inf() : double_float_type::my_value_inf()); + } + } + else + { + // pow(+infinity, exp) returns +0 for any negative exp. + // pow(+infinity, exp) returns +infinity for any positive exp. + + result = ((p < static_cast(0)) ? double_float_type { unsigned { UINT8_C(0) } } : double_float_type::my_value_inf()); + } + } + else + { + result = double_float_type::my_value_nan(); + } + } + else + { + double_float_type::pown(result, x, p); + } +} + template ::value && ((cpp_df_qf_detail::ccmath::numeric_limits::digits10 * 2) < 16))>::type const*> constexpr void eval_exp(cpp_double_fp_backend& result, const cpp_double_fp_backend& x) diff --git a/include/boost/multiprecision/detail/default_ops.hpp b/include/boost/multiprecision/detail/default_ops.hpp index bd0240aba..a11d17d10 100644 --- a/include/boost/multiprecision/detail/default_ops.hpp +++ b/include/boost/multiprecision/detail/default_ops.hpp @@ -2338,21 +2338,21 @@ template inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if::value == number_kind_complex, component_type>>::type::type abs(const number& v) { - return std::move(boost::math::hypot(real(v), imag(v))); + return boost::math::hypot(real(v), imag(v)); } template inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if::result_type>::value == number_kind_complex, component_type::result_type>>::type::type abs(const detail::expression& v) { using number_type = typename detail::expression::result_type; - return std::move(abs(static_cast(v))); + return abs(static_cast(v)); } template inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if::value == number_kind_complex, typename scalar_result_from_possible_complex >::type>::type arg(const number& v) { - return std::move(atan2(imag(v), real(v))); + return atan2(imag(v), real(v)); } template inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if::value == number_kind_floating_point, typename scalar_result_from_possible_complex >::type>::type @@ -2365,7 +2365,7 @@ inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if& v) { using number_type = typename detail::expression::result_type; - return std::move(arg(static_cast(v))); + return arg(static_cast(v)); } #endif // BOOST_MP_MATH_AVAILABLE @@ -2374,7 +2374,7 @@ inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if::valu norm(const number& v) { typename component_type >::type a(real(v)), b(imag(v)); - return std::move(a * a + b * b); + return a * a + b * b; } template inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if::value != number_kind_complex, typename scalar_result_from_possible_complex >::type>::type @@ -2387,7 +2387,7 @@ inline BOOST_MP_CXX14_CONSTEXPR typename scalar_result_from_possible_complex& v) { using number_type = typename detail::expression::result_type; - return std::move(norm(static_cast(v))); + return norm(static_cast(v)); } template diff --git a/include/boost/multiprecision/detail/functions/pow.hpp b/include/boost/multiprecision/detail/functions/pow.hpp index 9d5b7fa35..b428f0e90 100644 --- a/include/boost/multiprecision/detail/functions/pow.hpp +++ b/include/boost/multiprecision/detail/functions/pow.hpp @@ -43,6 +43,27 @@ inline void pow_imp(T& result, const T& t, const U& p, const std::integral_const return; } + switch (p) + { + case 0: + result = int_type(1); + return; + case 1: + result = t; + return; + case 2: + eval_multiply(result, t, t); + return; + case 3: + eval_multiply(result, t, t); + eval_multiply(result, t); + return; + case 4: + eval_multiply(result, t, t); + eval_multiply(result, result); + return; + } + // This will store the result. if (U(p % U(2)) != U(0)) { diff --git a/test/test_various_edges.cpp b/test/test_various_edges.cpp index 60cf1d659..70a254756 100644 --- a/test/test_various_edges.cpp +++ b/test/test_various_edges.cpp @@ -15,11 +15,15 @@ #include #endif +#include #include +#include +#include #include #include #include #include +#include #if defined(__clang__) # pragma clang diagnostic push @@ -426,6 +430,48 @@ namespace local BOOST_TEST(result_is_ok = ((conversion_result_max == (std::numeric_limits::max)()) && result_is_ok)); } + { + using str_nans_array_type = std::array; + + const str_nans_array_type str_nan_reps {{ "nan", "NaN", "NAN" }}; + + for(auto i = static_cast(UINT8_C(0)); i < str_nan_reps.size(); ++i) + { + float_type flt_nan { float_type { str_nan_reps[i].c_str() } * dis(gen) }; + + const bool result_nan_str_is_ok { isnan(flt_nan) }; + + BOOST_TEST(result_is_ok = (result_nan_str_is_ok && result_is_ok)); + } + } + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(32)); ++i) + { + static_cast(i); + + using ctrl_type = boost::multiprecision::number, boost::multiprecision::et_off>; + + const float fuzz { dis(gen) }; + + const float_type flt_tiny { (::std::numeric_limits::min)() * fuzz }; + const float_type flt_finite { (::std::numeric_limits::max)() * flt_tiny }; + const float_type flt_finite2 { flt_tiny * (::std::numeric_limits::max)() }; + + const ctrl_type ctrl_tiny { ctrl_type { (::std::numeric_limits::min)() } * fuzz }; + const ctrl_type ctrl_finite { ctrl_type { (::std::numeric_limits::max)() } * ctrl_tiny }; + + const bool + result_finite_is_ok + { + isfinite(flt_finite) + && isfinite(flt_finite2) + && local::is_close_fraction(flt_finite, float_type { ctrl_finite }, std::numeric_limits::epsilon() * 32) + && local::is_close_fraction(flt_finite2, float_type { ctrl_finite }, std::numeric_limits::epsilon() * 32) + }; + + BOOST_TEST(result_is_ok = (result_finite_is_ok && result_is_ok)); + } + return result_is_ok; } @@ -838,6 +884,274 @@ namespace local return result_is_ok; } + template + auto test_pow_n_edge() -> bool + { + using float_type = FloatType; + + std::mt19937_64 gen { time_point() }; + + std::uniform_real_distribution + dist + ( + static_cast(1.01L), + static_cast(1.04L) + ); + + std::uniform_int_distribution + dist_n + ( + static_cast(INT8_C(2)), + static_cast(INT8_C(12)) + ); + + using std::fpclassify; + using std::isinf; + using std::pow; + using std::signbit; + + auto result_is_ok = true; + + for(auto index = 0U; index < 8U; ++index) + { + static_cast(index); + + const float_type flt_x { static_cast(dist(gen)) }; + + const auto flt_nrm = pow(flt_x, 0); + + const auto result_val_pow_zero_is_ok = (flt_nrm == static_cast(1.0L)); + + BOOST_TEST(result_val_pow_zero_is_ok); + + result_is_ok = (result_val_pow_zero_is_ok && result_is_ok); + } + + for(auto index = 0U; index < 8U; ++index) + { + static_cast(index); + + const float_type arg_nan = ::my_nan() * static_cast(dist(gen)); + + const auto val_pow_nan = pow(arg_nan, dist_n(gen)); + + const auto result_val_pow_nan_is_ok = (isnan(val_pow_nan) && isnan(arg_nan)); + + BOOST_TEST(result_val_pow_nan_is_ok); + + result_is_ok = (result_val_pow_nan_is_ok && result_is_ok); + } + + for(auto index = 0U; index < 8U; ++index) + { + static_cast(index); + + const float_type arg_x_nrm = static_cast(dist(gen)); + const float_type arg_p_zero = static_cast(dist_n(gen)) * (::my_zero() * static_cast(dist(gen))); + const int n_p_zero = static_cast(arg_p_zero); + + const auto val_pow_zero = pow(arg_x_nrm, n_p_zero); + + const auto result_val_pow_zero_is_ok = ((val_pow_zero == float_type { 1 }) && (n_p_zero == 0)); + + BOOST_TEST(result_val_pow_zero_is_ok); + + result_is_ok = (result_val_pow_zero_is_ok && result_is_ok); + } + + for(auto index = 0U; index < 8U; ++index) + { + static_cast(index); + + using ctrl_type = boost::multiprecision::number, boost::multiprecision::et_off>; + + const float_type val_normal { float_type { float_type { 314 } / 100 } * static_cast(dist(gen)) }; + const ctrl_type val_ctrl { val_normal }; + + const int n_neg { -dist_n(gen) }; + + const float_type flt_zero_nrm { pow(val_normal, n_neg) }; + const ctrl_type ctl_val { pow(val_ctrl, n_neg) }; + + const bool + result_val_nrm_n_neg_is_ok + { + (fpclassify(flt_zero_nrm) == FP_NORMAL) + && local::is_close_fraction(flt_zero_nrm, float_type { ctl_val }, std::numeric_limits::epsilon() * 32) + }; + + BOOST_TEST(result_val_nrm_n_neg_is_ok); + + result_is_ok = (result_val_nrm_n_neg_is_ok && result_is_ok); + } + + for(auto index = 0U; index < 8U; ++index) + { + static_cast(index); + + using ctrl_type = boost::multiprecision::number, boost::multiprecision::et_off>; + + const float_type val_zero { ::my_zero() * static_cast(dist(gen)) }; + const ctrl_type val_ctrl { val_zero }; + + const int n_pos { dist_n(gen) }; + + const float_type flt_zero_pos { pow(val_zero, n_pos) }; + const ctrl_type flt_zero_ctrl { pow(val_ctrl, n_pos) }; + + const bool + result_val_zero_pos_is_ok + { + (fpclassify(flt_zero_pos) == FP_ZERO) && (fpclassify(flt_zero_pos) == (fpclassify(flt_zero_ctrl))) + }; + + BOOST_TEST(result_val_zero_pos_is_ok); + + result_is_ok = (result_val_zero_pos_is_ok && result_is_ok); + } + + for(auto index = 0U; index < 8U; ++index) + { + static_cast(index); + + using ctrl_type = boost::multiprecision::number, boost::multiprecision::et_off>; + + const float_type val_zero { ::my_zero() * static_cast(dist(gen)) }; + const ctrl_type val_ctrl { val_zero }; + + const int n_neg { -dist_n(gen) }; + + const float_type flt_zero_neg { pow(val_zero, n_neg) }; + const ctrl_type flt_zero_ctrl { pow(val_ctrl, n_neg) }; + + const bool result_val_zero_neg_is_ok { (isinf(flt_zero_neg) && isinf(flt_zero_ctrl)) }; + + BOOST_TEST(result_val_zero_neg_is_ok); + + result_is_ok = (result_val_zero_neg_is_ok && result_is_ok); + } + + for(auto index = static_cast(INT8_C(-10)); index <= static_cast(INT8_C(-2)); index += static_cast(INT8_C(2))) + { + const float_type val_zero { ::my_zero() * static_cast(dist(gen)) }; + + const auto flt_zero_pos = pow(val_zero, static_cast(index)); + + const auto result_val_zero_pos_is_ok = isinf(flt_zero_pos); + + BOOST_TEST(result_val_zero_pos_is_ok); + + result_is_ok = (result_val_zero_pos_is_ok && result_is_ok); + } + + for(auto index = static_cast(INT8_C(-11)); index <= static_cast(INT8_C(-3)); index += static_cast(INT8_C(2))) + { + const float_type val_something { ::my_one() * static_cast(dist(gen)) }; + + const int n_zero { static_cast(::my_zero() * static_cast(dist(gen))) }; + + const auto flt_pow_zero = pow(val_something, n_zero); + + const bool result_val_pow_zero_is_ok = { flt_pow_zero == float_type { 1.0F } }; + + BOOST_TEST(result_val_pow_zero_is_ok); + + result_is_ok = (result_val_pow_zero_is_ok && result_is_ok); + } + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) + { + static_cast(i); + + const auto flt_near_one = static_cast(dist(gen)); + + const auto flt_inf_pos = pow(std::numeric_limits::infinity() * flt_near_one, dist_n(gen)); + + const auto result_val_inf_pos_is_ok = (fpclassify(flt_inf_pos) == FP_INFINITE); + + BOOST_TEST(result_val_inf_pos_is_ok); + + result_is_ok = (result_val_inf_pos_is_ok && result_is_ok); + } + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) + { + static_cast(i); + + const auto flt_near_one = static_cast(dist(gen)); + + const auto flt_inf_pos_n_neg = pow(std::numeric_limits::infinity() * flt_near_one, -dist_n(gen)); + + const auto result_val_inf_pos_n_neg_is_ok = (fpclassify(flt_inf_pos_n_neg) == FP_ZERO); + + BOOST_TEST(result_val_inf_pos_n_neg_is_ok); + + result_is_ok = (result_val_inf_pos_n_neg_is_ok && result_is_ok); + } + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) + { + static_cast(i); + + const auto flt_near_one = static_cast(dist(gen)); + + const auto flt_inf_neg = pow(-std::numeric_limits::infinity() * flt_near_one, -3); + + const auto result_val_inf_neg_is_ok = (fpclassify(flt_inf_neg) == FP_ZERO); + + BOOST_TEST(result_val_inf_neg_is_ok); + + result_is_ok = (result_val_inf_neg_is_ok && result_is_ok); + } + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) + { + static_cast(i); + + const auto flt_near_one = static_cast(dist(gen)); + + const auto flt_inf_neg = pow(-std::numeric_limits::infinity() * flt_near_one, 3); + + const auto result_val_inf_neg_is_ok = (fpclassify(flt_inf_neg) == FP_INFINITE); + + BOOST_TEST(result_val_inf_neg_is_ok); + + result_is_ok = (result_val_inf_neg_is_ok && result_is_ok); + } + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) + { + static_cast(i); + + const auto flt_near_one = static_cast(dist(gen)); + + const auto flt_nan = pow(std::numeric_limits::quiet_NaN() * static_cast(flt_near_one), 0); + + const auto result_val_nan_is_ok = (flt_nan == float_type { 1.0F }); + + BOOST_TEST(result_val_nan_is_ok); + + result_is_ok = (result_val_nan_is_ok && result_is_ok); + } + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) + { + static_cast(i); + + const auto flt_near_one = static_cast(dist(gen)); + + const auto flt_nan = pow(std::numeric_limits::quiet_NaN() * static_cast(flt_near_one), i + 1); + + const auto result_val_nan_is_ok = isnan(flt_nan); + + BOOST_TEST(result_val_nan_is_ok); + + result_is_ok = (result_val_nan_is_ok && result_is_ok); + } + + return result_is_ok; + } + } // namespace local auto main() -> int @@ -851,6 +1165,7 @@ auto main() -> int local::test_sqrt_edge(); local::test_exp_edge(); local::test_log_edge(); + local::test_pow_n_edge(); } { @@ -861,6 +1176,7 @@ auto main() -> int local::test_sqrt_edge(); local::test_exp_edge(); local::test_log_edge(); + local::test_pow_n_edge(); } { @@ -871,6 +1187,7 @@ auto main() -> int local::test_sqrt_edge(); local::test_exp_edge(); local::test_log_edge(); + local::test_pow_n_edge(); } #endif @@ -883,6 +1200,7 @@ auto main() -> int local::test_sqrt_edge(); local::test_exp_edge(); local::test_log_edge(); + local::test_pow_n_edge(); } return boost::report_errors();