Merge pull request #732 from boostorg/more_double_fp_findings

Try handle more double_fp findings
This commit is contained in:
Christopher Kormanyos
2025-09-05 07:38:59 +02:00
committed by GitHub
4 changed files with 88 additions and 49 deletions

View File

@@ -1,7 +1,7 @@
///////////////////////////////////////////////////////////////////////////////
// Copyright 2021 Fahad Syed.
// Copyright 2021 - 2025 Fahad Syed.
// Copyright 2021 - 2025 Christopher Kormanyos.
// Copyright 2021 Janek Kozicki.
// Copyright 2021 - 2025 Janek Kozicki.
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE_1_0.txt or copy at
// http://www.boost.org/LICENSE_1_0.txt)
@@ -13,7 +13,6 @@
#include <boost/multiprecision/detail/standalone_config.hpp>
#if defined(BOOST_HAS_FLOAT128)
#if defined(__has_include)
#if __has_include(<quadmath.h>)
#include <quadmath.h>
@@ -23,7 +22,6 @@
#endif
#endif // __has_include(<quadmath.h>)
#endif // defined(__has_include)
#endif // defined(BOOST_HAS_FLOAT128)
#include <boost/multiprecision/number.hpp>
@@ -59,12 +57,14 @@ struct pair
float_type first;
float_type second;
constexpr pair() : first { }, second { } { };
constexpr pair(float_type a, float_type b) : first { a }, second { b } { }
constexpr pair(const pair& other) : first { other.first }, second { other.second } { }
// Default-constructed cpp_double_fp_backend values are zero.
constexpr pair() noexcept : first { }, second { } { }
constexpr pair(float_type a, float_type b) noexcept : first { a }, second { b } { }
constexpr pair(const pair& other) noexcept : first { other.first }, second { other.second } { }
constexpr pair(pair&& other) noexcept : first { other.first }, second { other.second } { }
constexpr auto operator=(const pair& other) -> pair&
constexpr auto operator=(const pair& other) noexcept -> pair&
{
if (this != &other)
{
@@ -110,7 +110,7 @@ public:
};
static_assert(n_shl < std::numeric_limits<std::uint64_t>::digits,
"Error: Left-shift amount for split does not fin in std::uint64_t");
"Error: Left-shift amount for split does not fit in std::uint64_t");
static constexpr float_type
value

View File

@@ -14,7 +14,7 @@
#include <cmath>
#include <type_traits>
#if (defined(__GNUC__) && defined(BOOST_MP_CPP_DOUBLE_FP_HAS_FLOAT128))
#if (defined(BOOST_GCC) && defined(BOOST_MP_CPP_DOUBLE_FP_HAS_FLOAT128))
//
// This is the only way we can avoid
// warning: non-standard suffix on floating constant [-Wpedantic]

View File

@@ -37,7 +37,7 @@
#include <string>
#include <type_traits>
#if (defined(__clang__) && (__clang_major__ <= 9))
#if (defined(BOOST_CLANG) && defined(BOOST_CLANG_VERSION) && (BOOST_CLANG_VERSION <= 90000))
#define BOOST_MP_DF_QF_NUM_LIMITS_CLASS_TYPE struct
#else
#define BOOST_MP_DF_QF_NUM_LIMITS_CLASS_TYPE class
@@ -137,9 +137,8 @@ template <typename FloatingPointType,
typename OtherFloatingPointType>
constexpr auto eval_convert_to(OtherFloatingPointType* result, const cpp_double_fp_backend<FloatingPointType>& backend) -> typename ::std::enable_if<cpp_df_qf_detail::is_floating_point<OtherFloatingPointType>::value>::type;
// TBD: constexpr on hash_value?
template <typename FloatingPointType>
auto hash_value(const cpp_double_fp_backend<FloatingPointType>& a) -> ::std::size_t;
constexpr auto hash_value(const cpp_double_fp_backend<FloatingPointType>& a) -> ::std::size_t;
template <typename FloatingPointType>
constexpr auto fabs(const cpp_double_fp_backend<FloatingPointType>& a) -> cpp_double_fp_backend<FloatingPointType>;
@@ -385,38 +384,25 @@ class cpp_double_fp_backend
return *this;
}
auto hash() const -> ::std::size_t
constexpr auto hash() const -> ::std::size_t
{
// Hash the raw values of the data field with direct-memory access.
// Use 16-bit (2 byte) chunks as the data size when hashing.
static_assert( ( sizeof(data.first) == sizeof(data.second))
&& ( sizeof(float_type) >= sizeof(std::uint16_t))
&& ((sizeof(float_type) % sizeof(std::uint16_t)) == std::size_t { UINT8_C(0) }),
"Error: float_type size is inappropriate for hashing routine");
auto hash_one
{
[](std::size_t& res, const float_type& val)
{
const std::uint16_t* first { reinterpret_cast<const std::uint16_t*>(&val) };
const std::uint16_t* last { first + std::size_t { sizeof(float_type) / sizeof(std::uint16_t) } };
while (first != last)
{
boost::multiprecision::detail::hash_combine(res, *first);
++first;
}
return res;
}
};
std::size_t result { UINT8_C(0) };
static_cast<void>(hash_one(result, data.first));
static_cast<void>(hash_one(result, data.second));
int n_first { };
int n_second { };
#if defined(BOOST_MP_CPP_DOUBLE_FP_HAS_FLOAT128)
using local_float_type = typename std::conditional<::std::is_same<float_type, ::boost::float128_type>::value,
long double,
float_type>::type;
#else
using local_float_type = float_type;
#endif
boost::multiprecision::detail::hash_combine(result, static_cast<local_float_type>(cpp_df_qf_detail::ccmath::frexp(data.first, &n_first)));
boost::multiprecision::detail::hash_combine(result, static_cast<local_float_type>(cpp_df_qf_detail::ccmath::frexp(data.second, &n_second)));
boost::multiprecision::detail::hash_combine(result, n_first);
boost::multiprecision::detail::hash_combine(result, n_second);
return result;
}
@@ -574,6 +560,21 @@ class cpp_double_fp_backend
data = arithmetic::two_diff(data.first, v.data.first);
if (cpp_df_qf_detail::ccmath::isinf(data.first))
{
// Handle overflow.
const bool b_neg { (data.first < float_type { 0.0F }) };
*this = cpp_double_fp_backend::my_value_inf();
if (b_neg)
{
negate();
}
return *this;
}
data = arithmetic::two_hilo_sum(data.first, data.second + thi_tlo.first);
data = arithmetic::two_hilo_sum(data.first, thi_tlo.second + data.second);
@@ -885,6 +886,10 @@ class cpp_double_fp_backend
}
}
// TBD: Exactly what compilers/language-standards are needed to make this constexpr?
// TBD: It odes not really become constexpr until we stop using an intermediate
// cpp_bin_float anyway. But I will leave this comment for future library evolution.
auto str(std::streamsize number_of_digits, const std::ios::fmtflags format_flags) const -> std::string
{
if (number_of_digits == 0)
@@ -1033,7 +1038,7 @@ class cpp_double_fp_backend
using cpp_bin_float_read_write_type = boost::multiprecision::number<cpp_bin_float_read_write_backend_type, boost::multiprecision::et_off>;
auto rd_string(const char* pstr) -> bool;
constexpr auto rd_string(const char* pstr) -> bool;
constexpr auto add_unchecked(const cpp_double_fp_backend& v) -> void
{
@@ -1041,6 +1046,21 @@ class cpp_double_fp_backend
data = arithmetic::two_sum(data.first, v.data.first);
if (cpp_df_qf_detail::ccmath::isinf(data.first))
{
// Handle overflow.
const bool b_neg { (data.first < float_type { 0.0F }) };
*this = cpp_double_fp_backend::my_value_inf();
if (b_neg)
{
negate();
}
return;
}
data = arithmetic::two_hilo_sum(data.first, data.second + thi_tlo.first);
data = arithmetic::two_hilo_sum(data.first, thi_tlo.second + data.second);
@@ -1156,7 +1176,7 @@ class cpp_double_fp_backend
};
template <typename FloatingPointType>
auto cpp_double_fp_backend<FloatingPointType>::rd_string(const char* pstr) -> bool
constexpr auto cpp_double_fp_backend<FloatingPointType>::rd_string(const char* pstr) -> bool
{
cpp_bin_float_read_write_type f_bin { pstr };
@@ -2458,7 +2478,7 @@ constexpr auto eval_convert_to(OtherFloatingPointType* result, const cpp_double_
}
template <typename FloatingPointType>
auto hash_value(const cpp_double_fp_backend<FloatingPointType>& a) -> ::std::size_t
constexpr auto hash_value(const cpp_double_fp_backend<FloatingPointType>& a) -> ::std::size_t
{
return a.hash();
}

View File

@@ -499,8 +499,6 @@ namespace local
result_is_ok = (result_unf_is_ok && result_is_ok);
}
// TBD: See open issues for reminder to get this working in cpp_double_fp_backend.
BOOST_IF_CONSTEXPR(!::has_poor_exp_range_or_precision_support<float_type>::value)
{
using std::ldexp;
@@ -524,8 +522,6 @@ namespace local
result_is_ok = (result_ovf_is_ok && result_is_ok);
}
// TBD: See open issues for reminder to get this working in cpp_double_fp_backend.
BOOST_IF_CONSTEXPR(!::has_poor_exp_range_or_precision_support<float_type>::value)
{
using std::ldexp;
@@ -549,6 +545,29 @@ namespace local
result_is_ok = (result_ovf_is_ok && result_is_ok);
}
{
using std::ldexp;
float_type flt_near_lowest { -ldexp((std::numeric_limits<float_type>::max)(), -1) };
const float_type neg_flt_less_near_max { -ldexp((std::numeric_limits<float_type>::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<int>(INT8_C(-16)); n_loop < static_cast<int>(INT8_C(-8)); ++n_loop)
{
using std::ldexp;