Skip to content

Commit

Permalink
make sure eval_convert_to() do not terminate with super large number
Browse files Browse the repository at this point in the history
this change is a follow-up of d51f2e9. it intends to
address the exception thrown in a noexcept functon.

a minimal reproducer looks like

```c++

int main() {
    std::string s = "32767456456456456456545678943512357658768763546575675";
    boost::multiprecision::cpp_int num(s);
    std::cout << num.convert_to<float>() << std::endl;
}
```

since boost 1.79, the code above terminates like
```
Program returned: 139
Program stderr
terminate called after throwing an instance of 'boost::wrapexcept<std::domain_error>'
  what():  Error in function float_next<float>(float): Argument must be finite, but got inf
Program terminated with signal: SIGSEGV
```

because `float_next_imp()` throws 'boost::wrapexcept<std::domain_error>'
if the number is NAN of INF. and `eval_convert_to()` is marked as
`noexcept(boost::multiprecision::detail::is_arithmetic<R>::value &&
          std::numeric_limits<R>::has_infinity)`,
but only `overflow_error` is ignored in the policy passed to
`float_next()`.

so, in this change, `std::domain_error` is ignored as well, so that
``num.convert_to<float>()` returns a NaN in this case.

Refs boostorg#553

Signed-off-by: Kefu Chai <tchaikov@gmail.com>
  • Loading branch information
tchaikov committed May 4, 2024
1 parent fe3054f commit ea78649
Showing 1 changed file with 6 additions and 3 deletions.
9 changes: 6 additions & 3 deletions include/boost/multiprecision/cpp_int/misc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,9 @@ eval_convert_to(R* result, const cpp_int_backend<MinBits1, MaxBits1, SignType1,

template <class R, std::size_t MinBits1, std::size_t MaxBits1, cpp_integer_type SignType1, cpp_int_check_type Checked1, class Allocator1>
inline BOOST_MP_CXX14_CONSTEXPR typename std::enable_if<std::is_floating_point<R>::value && !is_trivial_cpp_int<cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1> >::value, void>::type
eval_convert_to(R* result, const cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& backend) noexcept(boost::multiprecision::detail::is_arithmetic<R>::value && std::numeric_limits<R>::has_infinity)
eval_convert_to(R* result, const cpp_int_backend<MinBits1, MaxBits1, SignType1, Checked1, Allocator1>& backend) noexcept(boost::multiprecision::detail::is_arithmetic<R>::value &&
(std::numeric_limits<R>::has_infinity ||
std::numeric_limits<R>::has_quiet_NaN))
{
BOOST_MP_FLOAT128_USING using std::ldexp;
if (eval_is_zero(backend))
Expand Down Expand Up @@ -244,10 +246,11 @@ eval_convert_to(R* result, const cpp_int_backend<MinBits1, MaxBits1, SignType1,
if ((eval_lsb_imp(backend) < static_cast<std::size_t>(bits)) || eval_bit_test(backend, static_cast<std::size_t>(bits + 1)))
{
#ifdef BOOST_MP_MATH_AVAILABLE
BOOST_IF_CONSTEXPR(std::numeric_limits<R>::has_infinity)
BOOST_IF_CONSTEXPR(std::numeric_limits<R>::has_infinity || std::numeric_limits<R>::has_quiet_NaN)
{
// Must NOT throw:
*result = boost::math::float_next(*result, boost::math::policies::make_policy(boost::math::policies::overflow_error<boost::math::policies::ignore_error>()));
*result = boost::math::float_next(*result, boost::math::policies::make_policy(boost::math::policies::overflow_error<boost::math::policies::ignore_error>(),
boost::math::policies::domain_error<boost::math::policies::ignore_error>()));
}
else
{
Expand Down

0 comments on commit ea78649

Please sign in to comment.