Improve rd_string inf-check and add tests

This commit is contained in:
ckormanyos
2025-09-20 13:05:58 +02:00
parent 156c87822f
commit 156e03e11e
2 changed files with 136 additions and 69 deletions
+82 -69
View File
@@ -1161,6 +1161,8 @@ class cpp_double_fp_backend
template <typename FloatingPointType>
constexpr auto cpp_double_fp_backend<FloatingPointType>::rd_string(const char* p_str) -> bool
{
// Use an intermediate cpp_bin_float backend type for reading string input.
cpp_bin_float_read_write_backend_type f_bin { };
f_bin = p_str;
@@ -1197,96 +1199,107 @@ constexpr auto cpp_double_fp_backend<FloatingPointType>::rd_string(const char* p
}()
};
using cpp_bin_float_read_write_exp_type = typename cpp_bin_float_read_write_backend_type::exponent_type;
const auto is_zero_or_subnormal =
(
(fpc == FP_ZERO)
|| (expval_from_f_bin < static_cast<cpp_bin_float_read_write_exp_type>(local_double_fp_type::my_min_exponent))
|| (expval_from_f_bin < static_cast<typename cpp_bin_float_read_write_backend_type::exponent_type>(local_double_fp_type::my_min_exponent))
);
if (is_zero_or_subnormal)
{
data.first = float_type { 0.0F };
data.second = float_type { 0.0F };
return true;
}
else
float_type flt_inf_check_first { };
eval_convert_to(&flt_inf_check_first, f_bin);
bool is_definitely_inf { ((fpc == FP_INFINITE) || cpp_df_qf_detail::ccmath::isinf(flt_inf_check_first)) };
if (!is_definitely_inf)
{
bool is_definitely_inf { (fpc == FP_INFINITE) };
if (!is_definitely_inf)
if (flt_inf_check_first > my_value_max().my_first())
{
float_type flt_inf_check { };
cpp_bin_float_read_write_backend_type f_bin_inf_check(f_bin);
eval_convert_to(&flt_inf_check, f_bin);
eval_subtract(f_bin_inf_check, cpp_bin_float_read_write_backend_type(flt_inf_check_first));
if (flt_inf_check > my_value_max().my_first())
{
is_definitely_inf = true;
}
float_type flt_inf_check_second { };
eval_convert_to(&flt_inf_check_second, f_bin_inf_check);
is_definitely_inf = eval_gt(local_double_fp_type(flt_inf_check_first, flt_inf_check_second), my_value_max());
}
};
if (is_definitely_inf)
{
static_cast<void>(operator=(local_double_fp_type::my_value_inf()));
if (b_neg)
{
negate();
}
return true;
}
// The input string is normal. We will now extract its value.
data.first = float_type { 0.0F };
data.second = float_type { 0.0F };
constexpr int pow2_scaling_for_small_input { cpp_df_qf_detail::ccmath::numeric_limits<float_type>::digits };
const auto has_pow2_scaling_for_small_input =
(
expval_from_f_bin < static_cast<int>(local_double_fp_type::my_min_exponent + pow2_scaling_for_small_input)
);
if (has_pow2_scaling_for_small_input)
{
eval_ldexp(f_bin, f_bin, pow2_scaling_for_small_input);
}
using local_builtin_float_type = typename std::conditional<(sizeof(float_type) <= sizeof(double)), double, float_type>::type;
constexpr unsigned
digit_limit
{
static_cast<unsigned>
(
static_cast<int>
(
(local_double_fp_type::my_digits / cpp_df_qf_detail::ccmath::numeric_limits<local_builtin_float_type>::digits)
+ (((local_double_fp_type::my_digits % cpp_df_qf_detail::ccmath::numeric_limits<local_builtin_float_type>::digits) != 0) ? 1 : 0)
)
* cpp_df_qf_detail::ccmath::numeric_limits<local_builtin_float_type>::digits
)
};
if (is_definitely_inf)
{
static_cast<void>(operator=(local_double_fp_type::my_value_inf()));
for(auto i = static_cast<unsigned>(UINT8_C(0));
i < digit_limit;
i = static_cast<unsigned>(i + static_cast<unsigned>(cpp_df_qf_detail::ccmath::numeric_limits<local_builtin_float_type>::digits)))
{
local_builtin_float_type flt_part { };
if (b_neg) { negate(); }
}
else
{
data.first = float_type { 0.0F };
data.second = float_type { 0.0F };
eval_convert_to(&flt_part, f_bin);
constexpr int pow2_scaling_for_small_input { cpp_df_qf_detail::ccmath::numeric_limits<float_type>::digits };
eval_subtract(f_bin, cpp_bin_float_read_write_backend_type(flt_part));
const auto has_pow2_scaling_for_small_input =
(
expval_from_f_bin < static_cast<int>(local_double_fp_type::my_min_exponent + pow2_scaling_for_small_input)
);
if (has_pow2_scaling_for_small_input)
{
eval_ldexp(f_bin, f_bin, pow2_scaling_for_small_input);
}
using local_builtin_float_type = typename std::conditional<(sizeof(float_type) <= sizeof(double)), double, float_type>::type;
constexpr unsigned
digit_limit
{
static_cast<unsigned>
(
static_cast<int>
(
(local_double_fp_type::my_digits / cpp_df_qf_detail::ccmath::numeric_limits<local_builtin_float_type>::digits)
+ (((local_double_fp_type::my_digits % cpp_df_qf_detail::ccmath::numeric_limits<local_builtin_float_type>::digits) != 0) ? 1 : 0)
)
* cpp_df_qf_detail::ccmath::numeric_limits<local_builtin_float_type>::digits
)
};
for(auto i = static_cast<unsigned>(UINT8_C(0));
i < digit_limit;
i = static_cast<unsigned>(i + static_cast<unsigned>(cpp_df_qf_detail::ccmath::numeric_limits<local_builtin_float_type>::digits)))
{
local_builtin_float_type flt_part { };
eval_convert_to(&flt_part, f_bin);
eval_subtract(f_bin, cpp_bin_float_read_write_backend_type(flt_part));
eval_add(*this, local_double_fp_type { flt_part });
}
if (has_pow2_scaling_for_small_input)
{
eval_ldexp(*this, *this, -pow2_scaling_for_small_input);
}
if (b_neg) { negate(); }
}
eval_add(*this, local_double_fp_type { flt_part });
}
if (has_pow2_scaling_for_small_input)
{
eval_ldexp(*this, *this, -pow2_scaling_for_small_input);
}
if (b_neg) { negate(); }
return true;
}
+54
View File
@@ -966,6 +966,59 @@ namespace local
}
}
}
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<float_backend_larger_type, boost::multiprecision::et_off>;
std::mt19937_64 gen { time_point<typename std::mt19937_64::result_type>() };
auto dis =
std::uniform_real_distribution<double>
{
static_cast<double>(1.01),
static_cast<double>(1.04)
};
{
for(auto index = static_cast<unsigned>(UINT8_C(0)); index < static_cast<unsigned>(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<double>::epsilon() };
float_larger_type
flt_larger
(
float_larger_type((std::numeric_limits<boost::multiprecision::cpp_double_double>::max)())
/ dbl_fuzz
);
if(make_neg)
{
flt_larger = -flt_larger;
}
std::stringstream strm { };
strm << std::setprecision(std::numeric_limits<boost::multiprecision::cpp_double_double>::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
@@ -1006,6 +1059,7 @@ auto main() -> int
static_cast<void>(local::test_edges_ovf_und<double_float_type>());
static_cast<void>(local::test_edges_trig<double_float_type>());
local::test_frexp_edge<double_float_type>();
local::test_double_fp_string_gt_max();
}
{