// Copyright 2023 - 2025 Matt Borland // Copyright 2023 - 2025 Christopher Kormanyos // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt // Some parts of this test file have been taken from the Boost.Decimal project. #if !defined(TEST_CPP_DOUBLE_FLOAT) #define TEST_CPP_DOUBLE_FLOAT #endif #include #include #include #include // Note: include this AFTER the test-backends are defined #include #include #include #include #include #include #if (defined(__clang__) || defined(__GNUC__)) # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wfloat-equal" #endif #include template auto my_zero() noexcept -> FloatType&; template auto my_one () noexcept -> FloatType&; template auto my_inf () noexcept -> FloatType&; template auto my_nan () noexcept -> FloatType&; template auto my_generator_overflow() noexcept -> FloatType; template auto my_generator_underflow() noexcept -> FloatType; namespace local { template auto time_point() noexcept -> IntegralTimePointType { using local_integral_time_point_type = IntegralTimePointType; using local_clock_type = ClockType; typename local_clock_type::time_point tp_zero { }; const auto my_now = local_clock_type::now(); const auto duration { my_now - tp_zero }; const std::uintmax_t value { static_cast ( std::chrono::duration_cast(my_now - tp_zero).count() ) }; return static_cast(value); } template auto is_close_fraction(const NumericType& a, const NumericType& b, const NumericType& tol) noexcept -> bool { using std::fabs; auto result_is_ok = bool { }; NumericType delta { }; if(b == static_cast(0)) { delta = fabs(a - b); // LCOV_EXCL_LINE result_is_ok = (delta <= tol); // LCOV_EXCL_LINE } else { delta = fabs(1 - (a / b)); result_is_ok = (delta <= tol); } return result_is_ok; } template bool test_convert_and_back(const float epsilon_factor) { using any_float_type = AnyFloatType; using other_float_type = OtherFloatType; std::mt19937_64 gen { time_point() }; auto dis = std::uniform_real_distribution { static_cast(-1.5F), static_cast(1.5F) }; bool result_is_ok { true }; for(int n_loop = static_cast(INT8_C(0)); n_loop < static_cast(INT8_C(64)); ++n_loop) { static_cast(n_loop); using string_data_array_type = std::array; // Table[N[Exp[Pi EulerGamma*m], 100], {m, 1, 41, 10}] // 6.131114182422604828954743181715560166328314533478636289880930665602805209787080979043183175178343525 // 4.601860472890328982970020344164597095017995523702194983334509372565826509463730150117899613177592949*10^8 // 3.454041008184686240269652058630805927805404685705614002929256804267342980143530922708514273650048389*10^16 // 2.592516517287682590319488890844344913780990651072642232646840608443538857001634005575916140667342752*10^24 // 1.945877850460679347837790546862626024227287564971475316486097406542338497232351222296834286913627889*10^32 const string_data_array_type float_number_strings { std::string("6.131114182422604828954743181715560166328314533478636289880930665602805209787080979043183175178343525"), std::string("4.601860472890328982970020344164597095017995523702194983334509372565826509463730150117899613177592949E8"), std::string("3.454041008184686240269652058630805927805404685705614002929256804267342980143530922708514273650048389E16"), std::string("2.592516517287682590319488890844344913780990651072642232646840608443538857001634005575916140667342752E24"), std::string("1.945877850460679347837790546862626024227287564971475316486097406542338497232351222296834286913627889E32") }; for(std::size_t index { std::size_t { UINT8_C(0) }}; index < std::tuple_size::value; ++index) { const any_float_type start(boost::lexical_cast(float_number_strings[index]) * (dis(gen)* dis(gen))); const other_float_type other(static_cast(start)); const any_float_type backto(static_cast(other)); const bool result_of_trip_is_ok { local::is_close_fraction ( start, backto, any_float_type(std::numeric_limits::epsilon() * epsilon_factor) ) }; BOOST_TEST(result_of_trip_is_ok); result_is_ok = (result_of_trip_is_ok && result_is_ok); } } return result_is_ok; } template bool test_edges() { using float_type = FloatType; std::mt19937_64 gen { time_point() }; auto dis = std::uniform_real_distribution { static_cast(1.01F), static_cast(1.04F) }; bool result_is_ok { true }; using std::isinf; using std::isnan; using std::signbit; { for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(16)); ++index) { static_cast(index); float_type flt_non_finite { }; long double ldbl_non_finite { }; bool result_non_finite_is_ok { }; flt_non_finite = std::numeric_limits::quiet_NaN() * dis(gen); ldbl_non_finite = static_cast(flt_non_finite); result_non_finite_is_ok = (isnan)(ldbl_non_finite); BOOST_TEST(result_non_finite_is_ok); result_is_ok = (result_non_finite_is_ok && result_is_ok); flt_non_finite = std::numeric_limits::infinity() * dis(gen); ldbl_non_finite = static_cast(flt_non_finite); result_non_finite_is_ok = (isinf)(ldbl_non_finite); BOOST_TEST(result_non_finite_is_ok); result_is_ok = (result_non_finite_is_ok && result_is_ok); flt_non_finite = std::numeric_limits::infinity() * -dis(gen); ldbl_non_finite = static_cast(flt_non_finite); result_non_finite_is_ok = ((isinf)(ldbl_non_finite) && signbit(ldbl_non_finite)); BOOST_TEST(result_non_finite_is_ok); result_is_ok = (result_non_finite_is_ok && result_is_ok); flt_non_finite = float_type { (std::numeric_limits::max)() } * float_type { (std::numeric_limits::max)() } * dis(gen); ldbl_non_finite = static_cast(flt_non_finite); result_non_finite_is_ok = (isinf)(ldbl_non_finite); BOOST_TEST(result_non_finite_is_ok); result_is_ok = (result_non_finite_is_ok && result_is_ok); flt_non_finite = float_type { (std::numeric_limits::max)() } * float_type { (std::numeric_limits::max)() } * -dis(gen); ldbl_non_finite = static_cast(flt_non_finite); result_non_finite_is_ok = ((isinf)(ldbl_non_finite) && signbit(ldbl_non_finite)); BOOST_TEST(result_non_finite_is_ok); result_is_ok = (result_non_finite_is_ok && result_is_ok); } } #ifdef BOOST_HAS_INT128 // TBD: Is this supposed to work for cpp_double_double? BOOST_IF_CONSTEXPR(!::has_poor_exp_range_or_precision_support::value) { for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(64)); ++index) { float_type flt_n128 { float_type { (std::numeric_limits::max)() } * float_type { dis(gen) * 12345.0F } * float_type { dis(gen) * dis(gen) } }; const bool is_neg { ((index & 1U) != 0U) }; if(is_neg) { flt_n128 = -flt_n128; } boost::int128_type val_n128 { static_cast(flt_n128) }; bool result_val_n128_is_ok { }; const auto str128_maker { [](const float_type& flt) { std::stringstream strm { }; strm << std::fixed << flt; std::string str_local { strm.str() }; str_local = str_local.substr(std::size_t { UINT8_C(0) }, str_local.find('.')); return str_local; } }; const std::string str_ctrl { str128_maker(flt_n128) }; const std::string str_n128 { str128_maker(static_cast(val_n128)) }; // This test assumes that boost::int128_type is as wide or wider than signed long long. // Consider using a a kind of if-constexpr to verify this "up front". result_val_n128_is_ok = ( (str_n128 == str_ctrl) && ( (!is_neg) ? (val_n128 >= static_cast((std::numeric_limits::max)())) : (val_n128 <= static_cast(std::numeric_limits::lowest())) ) ); BOOST_TEST(result_val_n128_is_ok); result_is_ok = (result_val_n128_is_ok && result_is_ok); } } { for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(32)); ++index) { constexpr boost::int128_type my_max_val_n128 = (((static_cast(1) << (sizeof(boost::int128_type) * CHAR_BIT - 2)) - 1) << 1) + 1; constexpr boost::int128_type my_min_val_n128 = static_cast(-my_max_val_n128 - 1); const float_type flt_n128 { float_type { -my_max_val_n128 } - ::my_one() + float_type { ::my_zero() + float_type { dis(gen) } } - ::my_one() - ::my_one() }; const boost::int128_type val_n128 { static_cast(flt_n128) }; const bool result_val_n128_is_ok { (val_n128 == my_min_val_n128) }; BOOST_TEST(result_val_n128_is_ok); result_is_ok = (result_val_n128_is_ok && result_is_ok); } } { for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(16)); ++index) { static_cast(index); const float_type flt_under_one { float_type { ::my_one() / dis(gen) } / dis(gen) }; const bool result_under_one_is_ok { (static_cast(flt_under_one) == static_cast(INT8_C(0))) }; BOOST_TEST(result_under_one_is_ok); result_is_ok = (result_under_one_is_ok && result_is_ok); } } #endif { const std::initializer_list funky_strings_list { std::string("3.14u"), std::string("3.14l"), std::string("3.14U"), std::string("3.14L"), std::string("3.14ul"), std::string("3.14Ul"), std::string("3.14uL"), std::string("3.14UL"), std::string("3.14ull"), std::string("3.14Ull"), std::string("3.14ULl"), std::string("3.14ULL"), std::string("3.14uLL"), std::string("3.14ulL") }; const std::vector funky_strings(funky_strings_list); std::size_t funky_count { }; for(const auto& str : funky_strings) { try { const float_type flt_from_bad_string(str); static_cast(flt_from_bad_string); } catch(const std::runtime_error& excp) { static_cast(excp.what()); ++funky_count; } } const bool result_funky_strings_is_ok { (funky_count == funky_strings.size()) }; BOOST_TEST(result_funky_strings_is_ok); result_is_ok = (result_funky_strings_is_ok && result_is_ok); } { const std::initializer_list infty_strings_list { std::string("inf"), std::string("INF"), std::string("infinity"), std::string("INFINITY"), std::string("+inf"), std::string("+INF"), std::string("+infinity"), std::string("+INFINITY"), std::string("-inf"), std::string("-INF"), std::string("-infinity"), std::string("-INFINITY") }; const std::vector infty_strings(infty_strings_list); std::size_t infty_count { }; for(const auto& str : infty_strings) { const float_type flt_from_inf_string(str); const bool result_infty_strings_is_ok { (boost::multiprecision::isinf)(flt_from_inf_string) && ((infty_count < std::size_t { UINT8_C(8) }) ? (!signbit(flt_from_inf_string)) : signbit(flt_from_inf_string)) }; ++infty_count; BOOST_TEST(result_infty_strings_is_ok); result_is_ok = (result_infty_strings_is_ok && result_is_ok); } } { for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(16)); ++index) { static_cast(index); float_type flt_nrm { ::my_one() * dis(gen) }; const bool is_neg { ((index & 1U) != 0U) }; if(is_neg) { flt_nrm = -flt_nrm; } const float_type flt_zer { ::my_zero() * dis(gen) }; flt_nrm /= flt_zer; bool result_div_zero_is_ok { (boost::multiprecision::isinf)(flt_nrm) }; if(is_neg) { result_div_zero_is_ok = ((boost::multiprecision::signbit)(flt_nrm) && result_div_zero_is_ok); } BOOST_TEST(result_div_zero_is_ok); result_is_ok = (result_div_zero_is_ok && result_is_ok); } } { for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(16)); ++index) { static_cast(index); float_type flt_nrm { ::my_one() + 0.25F }; float_type flt_one { ::my_one() + ::my_zero() * dis(gen) }; const bool is_neg { ((index & 1U) != 0U) }; if(is_neg) { flt_one = -flt_one; } flt_nrm /= flt_one; bool result_div_one_is_ok { (is_neg ? (flt_nrm == -1.25F) : (flt_nrm == 1.25F)) }; BOOST_TEST(result_div_one_is_ok); result_is_ok = (result_div_one_is_ok && result_is_ok); } } constexpr unsigned max_index { 1024U }; { using std::ldexp; float_type flt_near_min { ldexp((std::numeric_limits::min)(), +64) }; unsigned index { }; while((index < max_index) && (!((boost::multiprecision::fpclassify)(flt_near_min) == FP_ZERO))) { flt_near_min /= static_cast(0xDEADBEEF); ++index; } const bool result_unf_is_ok { ((index > 1U) && (index < max_index)) }; BOOST_TEST(result_unf_is_ok); result_is_ok = (result_unf_is_ok && result_is_ok); } { using std::ldexp; float_type flt_near_min { ldexp((std::numeric_limits::min)(), +64) }; unsigned index { }; while((index < max_index) && (!((boost::multiprecision::fpclassify)(flt_near_min) == FP_ZERO))) { flt_near_min /= static_cast(100000000ULL - 3ULL); ++index; } const bool result_unf_is_ok { ((index > 1U) && (index < max_index)) }; BOOST_TEST(result_unf_is_ok); result_is_ok = (result_unf_is_ok && result_is_ok); } { using std::ldexp; float_type flt_near_max { ldexp((std::numeric_limits::max)(), -1) }; const float_type flt_less_near_max { ldexp((std::numeric_limits::max)(), -4) }; unsigned index { }; while((index < max_index) && (!(boost::multiprecision::isinf)(flt_near_max))) { flt_near_max += (flt_less_near_max * dis(gen)); ++index; } const bool result_ovf_is_ok { ((index > 1U) && (index < max_index)) }; BOOST_TEST(result_ovf_is_ok); result_is_ok = (result_ovf_is_ok && result_is_ok); } { using std::ldexp; float_type flt_near_lowest { -ldexp((std::numeric_limits::max)(), -1) }; const float_type flt_less_near_max { ldexp((std::numeric_limits::max)(), -4) }; unsigned index { }; while((index < max_index) && (!(boost::multiprecision::isinf)(flt_near_lowest))) { flt_near_lowest -= (flt_less_near_max * dis(gen)); ++index; } const bool result_ovf_is_ok { ((index > 1U) && (index < max_index)) && signbit(flt_near_lowest) }; BOOST_TEST(result_ovf_is_ok); result_is_ok = (result_ovf_is_ok && result_is_ok); } { using std::ldexp; float_type flt_near_lowest { -ldexp((std::numeric_limits::max)(), -1) }; const float_type neg_flt_less_near_max { -ldexp((std::numeric_limits::max)(), -4) }; unsigned index { }; while((index < max_index) && (!(boost::multiprecision::isinf)(flt_near_lowest))) { flt_near_lowest += (neg_flt_less_near_max * dis(gen)); ++index; } const bool result_ovf_is_ok { ((index > 1U) && (index < max_index)) && signbit(flt_near_lowest) }; BOOST_TEST(result_ovf_is_ok); result_is_ok = (result_ovf_is_ok && result_is_ok); } for(int n_loop = static_cast(INT8_C(-16)); n_loop < static_cast(INT8_C(-8)); ++n_loop) { using std::ldexp; using std::fabs; float_type flt_near_max { ldexp((std::numeric_limits::max)(), n_loop) * dis(gen) }; unsigned index { }; while((index < max_index) && (!(boost::multiprecision::isinf)(flt_near_max))) { flt_near_max *= static_cast(INT32_C(2)); ++index; } const bool result_ovf_is_ok { ((index > 1U) && (index < max_index)) }; BOOST_TEST(result_ovf_is_ok); result_is_ok = (result_ovf_is_ok && result_is_ok); } { for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(16)); ++index) { static_cast(index); const float_type flt_nan_denominator { float_type { std::numeric_limits::quiet_NaN() } * dis(gen) }; float_type flt_zero_numerator { ::my_zero() * dis(gen) }; const bool result_div_zero_by_nan_is_ok { (boost::multiprecision::isnan)(flt_nan_denominator) && (boost::multiprecision::isnan)(flt_zero_numerator /= flt_nan_denominator) }; BOOST_TEST(result_div_zero_by_nan_is_ok); result_is_ok = (result_div_zero_by_nan_is_ok && result_is_ok); } } { const float_type flt_nan { ::my_nan() }; const float_type flt_inf { ::my_inf() }; for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(16)); ++index) { static_cast(index); flt_nan * dis(gen); flt_inf * dis(gen); const float_type finite { float_type { 123 } / 100 }; const float_type eq_nan = finite + flt_nan; const float_type eq_inf = finite + flt_inf; const bool result_nan_is_ok = (boost::multiprecision::isnan)(eq_nan); const bool result_inf_is_ok = (boost::multiprecision::isinf)(eq_inf); BOOST_TEST(result_nan_is_ok); BOOST_TEST(result_inf_is_ok); } } { for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(16)); ++index) { static_cast(index); float_type flt_zer_numerator { ::my_zero() * dis(gen) }; float_type flt_nrm_numerator { ::my_one() * dis(gen) }; float_type flt_nan_numerator { ::my_nan() * dis(gen) }; float_type flt_inf_numerator { ::my_inf() * dis(gen) }; const float_type flt_inf_result_neg_01 { -flt_nrm_numerator /= static_cast(INT8_C(0)) }; const float_type flt_inf_result_neg_02 { -flt_inf_numerator *= static_cast(index + 2U) }; const bool result_edge_00 { (boost::multiprecision::isnan)(flt_zer_numerator /= static_cast(INT8_C(0))) }; const bool result_edge_01 { (boost::multiprecision::isinf)(flt_nrm_numerator /= static_cast(INT8_C(0))) }; const bool result_edge_02 { (boost::multiprecision::isinf)(flt_inf_result_neg_01) && (boost::multiprecision::signbit)(flt_inf_result_neg_01) }; const bool result_edge_03 { (boost::multiprecision::isnan)(flt_nan_numerator /= static_cast(index + 2U)) }; const bool result_edge_04 { (boost::multiprecision::isnan)(flt_nan_numerator *= static_cast(index + 2U)) }; const bool result_edge_05 { (boost::multiprecision::isnan)(flt_inf_numerator *= static_cast(0)) }; const bool result_edge_06 { (boost::multiprecision::isinf)(flt_inf_result_neg_02) && (boost::multiprecision::signbit)(flt_inf_result_neg_02) }; const bool result_divs_are_ok { ( result_edge_00 && result_edge_01 && result_edge_02 && result_edge_03 && result_edge_04 && result_edge_05 && result_edge_06 ) }; BOOST_TEST(result_divs_are_ok); result_is_ok = (result_divs_are_ok && result_is_ok); } } BOOST_IF_CONSTEXPR((std::numeric_limits::digits >= 24) && std::numeric_limits::is_specialized) { for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(16)); ++index) { // This test is specifically designed to pick up hard-to-reach lines // in cpp_dec_float. Notice how the printed string is expected to have // (or not have) rounding at the 8th fixed-point digit. // N[1 + 1/2^4 + 1/2^5 + 1/2^7 + 1/2^9, 20] const float flt_builtin { 1.1035156250000000000F }; const float_type delta = std::numeric_limits::epsilon() * dis(gen) * static_cast(index + 1U); const float_type flt_val_base { flt_builtin }; const float_type flt_val_close_upper { flt_builtin + delta }; const float_type flt_val_close_lower { flt_builtin - delta }; { std::stringstream strm { }; strm << std::fixed << std::setprecision(std::streamsize { INT8_C(8) }) << flt_val_base; BOOST_TEST(strm.str() == "1.10351562"); } { std::stringstream strm { }; strm << std::fixed << std::setprecision(std::streamsize { INT8_C(8) }) << flt_val_close_upper; BOOST_TEST(strm.str() > "1.10351562"); } { std::stringstream strm { }; strm << std::fixed << std::setprecision(std::streamsize { INT8_C(8) }) << flt_val_close_lower; BOOST_TEST(strm.str() == "1.10351562"); } } } return result_is_ok; } template bool test_edges_ovf_und() { using float_type = FloatType; std::mt19937_64 gen { time_point() }; auto dis = std::uniform_real_distribution { static_cast(1.01F), static_cast(1.04F) }; bool result_is_ok { true }; { for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(16)); ++index) { static_cast(index); const float_type flt_ovf = ::my_generator_overflow() * dis(gen); const float_type flt_und = ::my_generator_underflow() * dis(gen); const bool result_ovf_und_is_ok { ((boost::multiprecision::isinf)(flt_ovf) && ((boost::multiprecision::fpclassify)(flt_und) == FP_ZERO)) }; BOOST_TEST(result_ovf_und_is_ok); result_is_ok = (result_ovf_und_is_ok && result_is_ok); } } return result_is_ok; } template bool test_edges_trig() { using float_type = FloatType; std::mt19937_64 gen { time_point() }; auto dis = std::uniform_real_distribution { static_cast(1.01F), static_cast(1.04F) }; bool result_is_ok { true }; using std::isinf; using std::isnan; using std::signbit; BOOST_IF_CONSTEXPR(std::numeric_limits::has_quiet_NaN) { for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(8)); ++index) { static_cast(index); float_type flt_nan = std::numeric_limits::quiet_NaN() * dis(gen); const float_type atan_nan = atan(flt_nan); const bool result_atan_nan_is_ok { (boost::multiprecision::isnan)(atan_nan) && ((boost::multiprecision::fpclassify)(atan_nan) == FP_NAN) }; BOOST_TEST(result_atan_nan_is_ok); result_is_ok = (result_atan_nan_is_ok && result_is_ok); } } return result_is_ok; } template auto test_convert_and_back_caller(const float epsilon_factor = 0.0F) -> bool { using other_float_type = OtherFloatType; std::cout << "Testing type (in test_convert_and_back): " << typeid(other_float_type).name() << std::endl; const bool result_of_trip_is_ok { local::test_convert_and_back(epsilon_factor) }; BOOST_TEST(result_of_trip_is_ok); return result_of_trip_is_ok; } template auto test_cpp_dec_float_rd_ovf_unf() -> void { using local_cpp_dec_float_type = CppDecFloatType; { std::string str_max_pos_01("+1.0E+"); std::string str_max_pos_02("+2.0E+"); std::string str_max_pos_03("+3.0E+"); std::string str_max_pos_04("+0.9E+"); std::string str_max_neg_01("-1.0E+"); std::string str_max_neg_02("-2.0E+"); std::string str_max_neg_03("-3.0E+"); std::string str_max_neg_04("-0.9E+"); { std::stringstream strm { }; strm << std::numeric_limits::max_exponent10; str_max_pos_01 += strm.str(); str_max_pos_02 += strm.str(); str_max_pos_03 += strm.str(); str_max_pos_04 += strm.str(); str_max_neg_01 += strm.str(); str_max_neg_02 += strm.str(); str_max_neg_03 += strm.str(); str_max_neg_04 += strm.str(); } const local_cpp_dec_float_type max_pos_01(str_max_pos_01); const local_cpp_dec_float_type max_pos_02(str_max_pos_02); const local_cpp_dec_float_type max_pos_03(str_max_pos_03); const local_cpp_dec_float_type max_pos_04(str_max_pos_04); const local_cpp_dec_float_type max_neg_01(str_max_neg_01); const local_cpp_dec_float_type max_neg_02(str_max_neg_02); const local_cpp_dec_float_type max_neg_03(str_max_neg_03); const local_cpp_dec_float_type max_neg_04(str_max_neg_04); using std::signbit; BOOST_TEST(max_pos_01 == (std::numeric_limits::max)()); BOOST_TEST((boost::multiprecision::isinf)(max_pos_02) && (!signbit(max_pos_02))); BOOST_TEST((boost::multiprecision::isinf)(max_pos_03) && (!signbit(max_pos_03))); BOOST_TEST(((boost::multiprecision::fpclassify)(max_pos_04) == FP_NORMAL) && (!signbit(max_pos_04))); BOOST_TEST(max_neg_01 == (std::numeric_limits::lowest)()); BOOST_TEST((boost::multiprecision::isinf)(max_neg_02) && signbit(max_neg_02)); BOOST_TEST((boost::multiprecision::isinf)(max_neg_03) && signbit(max_neg_03)); BOOST_TEST(((boost::multiprecision::fpclassify)(max_neg_04) == FP_NORMAL) && signbit(max_neg_04)); } { std::string str_min_pos_01("+1.0E"); std::string str_min_pos_02("+1.1E"); std::string str_min_pos_03("+0.9E"); std::string str_min_neg_01("-1.0E"); std::string str_min_neg_02("-1.1E"); std::string str_min_neg_03("-0.9E"); { std::stringstream strm { }; strm << std::numeric_limits::min_exponent10; str_min_pos_01 += strm.str(); str_min_pos_02 += strm.str(); str_min_pos_03 += strm.str(); str_min_neg_01 += strm.str(); str_min_neg_02 += strm.str(); str_min_neg_03 += strm.str(); } const local_cpp_dec_float_type min_pos_01(str_min_pos_01); const local_cpp_dec_float_type min_pos_02(str_min_pos_02); const local_cpp_dec_float_type min_pos_03(str_min_pos_03); const local_cpp_dec_float_type min_neg_01(str_min_neg_01); const local_cpp_dec_float_type min_neg_02(str_min_neg_02); const local_cpp_dec_float_type min_neg_03(str_min_neg_03); BOOST_TEST(min_pos_01 == (std::numeric_limits::min)()); BOOST_TEST(((boost::multiprecision::fpclassify)(min_pos_02) == FP_NORMAL) && (!signbit(min_pos_02))); BOOST_TEST(((boost::multiprecision::fpclassify)(min_pos_03) == FP_ZERO) && (!signbit(min_pos_03))); BOOST_TEST(min_neg_01 == -(std::numeric_limits::min)()); BOOST_TEST(((boost::multiprecision::fpclassify)(min_neg_02) == FP_NORMAL) && signbit(min_neg_02)); BOOST_TEST(((boost::multiprecision::fpclassify)(min_neg_03) == FP_ZERO) && (!signbit(min_neg_03))); } } template auto test_frexp_edge() -> void { using float_type = AnyFloatType; using float_backend_ctrl_type = boost::multiprecision::cpp_bin_float<50, boost::multiprecision::digit_base_10, void, std::int32_t>; using float_ctrl_type = boost::multiprecision::number; std::mt19937_64 gen { time_point() }; auto dis = std::uniform_real_distribution { static_cast(1.01F), static_cast(1.04F) }; { for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(32)); ++index) { static_cast(index); using local_exponent_type = typename float_type::backend_type::exponent_type; local_exponent_type exp_val { }; std::int32_t exp_val_ctrl { }; float_type arg_near_max { (std::numeric_limits::max)() / dis(gen) }; const bool is_neg { ((index & 1U) != 0U) }; if(is_neg) { arg_near_max = -arg_near_max; } float_type frexp_near_max { frexp(arg_near_max, &exp_val) }; const float_ctrl_type frexp_near_max_ctrl { frexp(float_ctrl_type(arg_near_max), &exp_val_ctrl) }; BOOST_TEST((boost::multiprecision::isfinite)(frexp_near_max)); BOOST_TEST(exp_val >= (std::numeric_limits::max_exponent - 8)); BOOST_TEST(exp_val == exp_val_ctrl); BOOST_TEST(is_close_fraction(float_type(frexp_near_max_ctrl), frexp_near_max, std::numeric_limits::epsilon() * 8)); } } } auto test_double_fp_string_gt_max() -> void { using float_backend_larger_type = boost::multiprecision::cpp_bin_float<50, boost::multiprecision::digit_base_10, void, std::int32_t>; using float_larger_type = boost::multiprecision::number; std::mt19937_64 gen { time_point() }; auto dis = std::uniform_real_distribution { static_cast(1.01), static_cast(1.04) }; { for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(32)); ++index) { const bool make_neg { ((index % unsigned { UINT8_C(2) }) != unsigned { UINT8_C(0) }) }; const double dbl_fuzz { 1.0 - dis(gen) * std::numeric_limits::epsilon() }; float_larger_type flt_larger ( float_larger_type((std::numeric_limits::max)()) / dbl_fuzz ); if(make_neg) { flt_larger = -flt_larger; } std::stringstream strm { }; strm << std::setprecision(std::numeric_limits::max_digits10) << flt_larger; const std::string str_ovf { strm.str() }; const boost::multiprecision::cpp_double_double val_ovf(str_ovf.c_str()); BOOST_TEST((boost::multiprecision::isinf)(val_ovf)); if(make_neg) { BOOST_TEST((boost::multiprecision::signbit)(val_ovf)); } } } } } // namespace local auto main() -> int { { using dec_float_backend_type = boost::multiprecision::cpp_dec_float<50>; using dec_float_type = boost::multiprecision::number; std::cout << "Testing type: " << typeid(dec_float_type).name() << std::endl; static_cast(local::test_edges()); static_cast(local::test_edges_ovf_und()); static_cast(local::test_edges_trig()); local::test_cpp_dec_float_rd_ovf_unf(); local::test_convert_and_back(0.0F); local::test_frexp_edge(); } { using bin_float_backend_type = boost::multiprecision::cpp_bin_float<50>; using bin_float_type = boost::multiprecision::number; std::cout << "Testing type: " << typeid(bin_float_type).name() << std::endl; static_cast(local::test_edges()); // TBD: This seemingly trivial test fails for cpp_bin_float. //static_cast(local::test_edges_ovf_und()); static_cast(local::test_edges_trig()); local::test_frexp_edge(); } { using double_float_type = boost::multiprecision::cpp_double_double; std::cout << "Testing type: " << typeid(double_float_type).name() << std::endl; static_cast(local::test_edges()); static_cast(local::test_edges_ovf_und()); static_cast(local::test_edges_trig()); local::test_frexp_edge(); local::test_double_fp_string_gt_max(); } { using big_bin_float_backend_type = boost::multiprecision::cpp_bin_float<512>; using big_dec_float_backend_type = boost::multiprecision::cpp_dec_float<512>; using big_bin_float_type = boost::multiprecision::number; using big_dec_float_type = boost::multiprecision::number; static_cast(local::test_convert_and_back_caller(0.25F)); static_cast(local::test_convert_and_back_caller(0.50F)); } return boost::report_errors(); } template auto my_zero() noexcept -> FloatType& { using float_type = FloatType; static float_type val_zero { 0 }; return val_zero; } template auto my_one () noexcept -> FloatType& { using float_type = FloatType; static float_type val_one { 1 }; return val_one; } template auto my_inf () noexcept -> FloatType& { using float_type = FloatType; static float_type val_inf { std::numeric_limits::infinity() }; return val_inf; } template auto my_nan () noexcept -> FloatType& { using float_type = FloatType; static float_type val_nan { std::numeric_limits::quiet_NaN() }; return val_nan; } template static auto overflow_expval_maker { []() { std::stringstream strm { }; using float_type = FloatType; strm << std::numeric_limits::max_exponent10 + 4; return strm.str(); } }; template static auto underflow_expval_maker { []() { std::stringstream strm { }; using float_type = FloatType; strm << std::numeric_limits::min_exponent10 - 4; return strm.str(); } }; template auto my_generator_overflow() noexcept -> FloatType { using float_type = FloatType; const float_type flt_ovf("1.23E" + overflow_expval_maker()); const float_type result(flt_ovf); return result; } template auto my_generator_underflow() noexcept -> FloatType { using float_type = FloatType; float_type flt_und("1.23E" + underflow_expval_maker()); const float_type result(flt_und); return result; } #if (defined(__clang__) || defined(__GNUC__)) # pragma GCC diagnostic pop #endif