From f8577ab70cd99a44da3f1f1d7eda85ca0ac99313 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Sun, 12 Jan 2025 12:08:41 +0100 Subject: [PATCH 01/16] Cover/syntax and remove useless I/O-stream ops --- .../boost/multiprecision/cpp_double_fp.hpp | 61 ++++------ test/test_exp.cpp | 106 +++++++++++++++++- 2 files changed, 127 insertions(+), 40 deletions(-) diff --git a/include/boost/multiprecision/cpp_double_fp.hpp b/include/boost/multiprecision/cpp_double_fp.hpp index 4b94b9e42..e833c3869 100644 --- a/include/boost/multiprecision/cpp_double_fp.hpp +++ b/include/boost/multiprecision/cpp_double_fp.hpp @@ -122,12 +122,6 @@ template constexpr typename ::std::enable_if::value>::type eval_convert_to(OtherFloatingPointType* result, const cpp_double_fp_backend& backend); -template -::std::basic_ostream& operator<<(std::basic_ostream& os, - const cpp_double_fp_backend& f); - template ::std::size_t hash_value(const cpp_double_fp_backend& a); @@ -356,20 +350,34 @@ class cpp_double_fp_backend std::size_t hash() const { - // Here we first convert to scientific string, then - // hash the charactgers in the scientific string. + // Hash the raw values of the data field with direct-memory access. + // Use 32-bit (4 byte) chunks as the data size when hashing. + + static_assert((sizeof(data.first) == sizeof(data.second)) && (sizeof(data.first) >= sizeof(std::uint32_t)), + "Error: data size is inappropriate for hashing routine"); + + auto hash_one + { + [](std::size_t& res, const float_type& val) + { + const std::uint32_t* first { reinterpret_cast(&val) }; + const std::uint32_t* last { reinterpret_cast(&val) + sizeof(float_type) / sizeof(std::uint32_t) }; + + while (first != last) + { + boost::multiprecision::detail::hash_combine(res, *first); - // TBD: Is there a faster or more simple hash method? - // TBD: Is there any constexpr support for rudimentary hashing? + ++first; + } - const std::string str_to_hash { str(cpp_double_fp_backend::my_digits10, std::ios::scientific) }; + return res; + } + }; std::size_t result { UINT8_C(0) }; - for (std::size_t i = std::size_t { UINT8_C(0) }; i < str_to_hash.length(); ++i) - { - boost::multiprecision::detail::hash_combine(result, str_to_hash.at(i)); - } + static_cast(hash_one(result, data.first)); + static_cast(hash_one(result, data.second)); return result; } @@ -1270,29 +1278,6 @@ constexpr cpp_double_fp_backend operator*(const cpp_double_fp template constexpr cpp_double_fp_backend operator/(const cpp_double_fp_backend& a, const cpp_double_fp_backend& b) { return cpp_double_fp_backend(a) /= b; } -// Input/Output Streaming -template -::std::basic_ostream& -operator<<(::std::basic_ostream& os, const cpp_double_fp_backend& f) -{ - const auto str_result = f.str(os.precision(), os.flags()); - - return (os << str_result); -} - -template -::std::basic_istream& -operator>>(::std::basic_istream& is, cpp_double_fp_backend& f) -{ - std::string input_str; - - is >> input_str; - - f = input_str.c_str(); - - return is; -} - template constexpr void eval_add(cpp_double_fp_backend& result, const cpp_double_fp_backend& x) { result += x; } template diff --git a/test/test_exp.cpp b/test/test_exp.cpp index 11328b319..a4ce87331 100644 --- a/test/test_exp.cpp +++ b/test/test_exp.cpp @@ -15,9 +15,11 @@ #endif #include -#include #include "test.hpp" +#include +#include + #if !defined(TEST_MPF_50) && !defined(TEST_MPF) && !defined(TEST_BACKEND) && !defined(TEST_CPP_DEC_FLOAT) && !defined(TEST_MPFR) && !defined(TEST_MPFR_50) && !defined(TEST_MPFI_50) && !defined(TEST_FLOAT128) && !defined(TEST_CPP_BIN_FLOAT) && !defined(TEST_CPP_DOUBLE_FLOAT) #define TEST_MPF_50 //# define TEST_MPF @@ -210,7 +212,6 @@ void test() } } - #if defined(TEST_CPP_DOUBLE_FLOAT) // Handle uneven/asymmetric exponents on min/max of cpp_double_fp_backend bug_case = log(1 / (std::numeric_limits::min)()) / -1.0005; @@ -227,6 +228,95 @@ void test() } } +template auto my_zero() -> FloatType&; +template auto my_one () -> FloatType&; + +template +auto test_exp_edge() -> bool +{ + using float_type = FloatType; + + std::mt19937_64 gen; + + std::uniform_real_distribution + dist + ( + static_cast(1.01L), + static_cast(1.04L) + ); + + auto result_is_ok = true; + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(4)); ++i) + { + static_cast(i); + + const float_type val_nan { exp(std::numeric_limits::quiet_NaN() * static_cast(dist(gen))) }; + + const bool result_val_nan_is_ok { isnan(val_nan) }; + + 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(4)); ++i) + { + static_cast(i); + + const float_type arg_inf { std::numeric_limits::infinity() * static_cast(dist(gen)) }; + + const float_type val_inf_pos { exp(arg_inf) }; + + const bool result_val_inf_pos_is_ok { (fpclassify(val_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(4)); ++i) + { + static_cast(i); + + const float_type val_inf_neg { exp(-std::numeric_limits::infinity() * static_cast(dist(gen))) }; + + const bool result_val_inf_neg_is_ok { (val_inf_neg == ::my_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(4)); ++i) + { + static_cast(i); + + const float_type val_zero_pos { exp(::my_zero()) }; + + const bool result_val_zero_pos_is_ok { (val_zero_pos == ::my_one()) }; + + BOOST_TEST(result_val_zero_pos_is_ok); + + result_is_ok = (result_val_zero_pos_is_ok && result_is_ok); + } + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(4)); ++i) + { + static_cast(i); + + const float_type val_zero_neg { exp(-::my_zero()) }; + + const bool result_val_zero_neg_is_ok { (val_zero_neg == ::my_one()) }; + + BOOST_TEST(result_val_zero_neg_is_ok); + + result_is_ok = (result_val_zero_neg_is_ok && result_is_ok); + } + + return result_is_ok; +} + int main() { #ifdef TEST_BACKEND @@ -247,6 +337,7 @@ int main() #ifdef TEST_CPP_DEC_FLOAT test(); test(); + test_exp_edge(); #ifndef SLOW_COMPILER // Some "peculiar" digit counts which stress our code: test > >(); @@ -268,6 +359,7 @@ int main() #ifdef TEST_CPP_BIN_FLOAT test(); test, long long> > >(); + test_exp_edge(); #endif #ifdef TEST_CPP_DOUBLE_FLOAT test(); @@ -276,6 +368,16 @@ int main() #if defined(BOOST_HAS_FLOAT128) test(); #endif + + test_exp_edge(); + test_exp_edge(); + test_exp_edge(); + #if defined(BOOST_HAS_FLOAT128) + test_exp_edge(); + #endif #endif return boost::report_errors(); } + +template auto my_zero() -> FloatType& { using float_type = FloatType; static float_type val_zero { 0 }; return val_zero; } +template auto my_one () -> FloatType& { using float_type = FloatType; static float_type val_one { 1 }; return val_one; } From 9d1359819158a5ea2dce18449abc28e2c0f94ccf Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Sun, 12 Jan 2025 13:00:18 +0100 Subject: [PATCH 02/16] Handle error on const-correctness --- include/boost/multiprecision/cpp_double_fp.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/boost/multiprecision/cpp_double_fp.hpp b/include/boost/multiprecision/cpp_double_fp.hpp index e833c3869..d6f5b5332 100644 --- a/include/boost/multiprecision/cpp_double_fp.hpp +++ b/include/boost/multiprecision/cpp_double_fp.hpp @@ -360,8 +360,8 @@ class cpp_double_fp_backend { [](std::size_t& res, const float_type& val) { - const std::uint32_t* first { reinterpret_cast(&val) }; - const std::uint32_t* last { reinterpret_cast(&val) + sizeof(float_type) / sizeof(std::uint32_t) }; + const std::uint32_t* first { reinterpret_cast(&val) }; + const std::uint32_t* last { first + std::size_t { sizeof(float_type) / sizeof(std::uint32_t) } }; while (first != last) { From df82a0c76bd4fcf3485fa73ad358a701b060622c Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Sun, 12 Jan 2025 18:38:33 +0100 Subject: [PATCH 03/16] Add more edge-case testing --- .../boost/multiprecision/cpp_double_fp.hpp | 12 +- test/Jamfile.v2 | 1 + test/test_arithmetic.hpp | 19 ++ test/test_exp.cpp | 100 +------ test/test_various_edges.cpp | 245 ++++++++++++++++++ 5 files changed, 273 insertions(+), 104 deletions(-) create mode 100644 test/test_various_edges.cpp diff --git a/include/boost/multiprecision/cpp_double_fp.hpp b/include/boost/multiprecision/cpp_double_fp.hpp index d6f5b5332..82f785723 100644 --- a/include/boost/multiprecision/cpp_double_fp.hpp +++ b/include/boost/multiprecision/cpp_double_fp.hpp @@ -351,17 +351,19 @@ class cpp_double_fp_backend std::size_t hash() const { // Hash the raw values of the data field with direct-memory access. - // Use 32-bit (4 byte) chunks as the data size when hashing. + // Use 16-bit (2 byte) chunks as the data size when hashing. - static_assert((sizeof(data.first) == sizeof(data.second)) && (sizeof(data.first) >= sizeof(std::uint32_t)), - "Error: data size is inappropriate for hashing routine"); + 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::uint32_t* first { reinterpret_cast(&val) }; - const std::uint32_t* last { first + std::size_t { sizeof(float_type) / sizeof(std::uint32_t) } }; + const std::uint16_t* first { reinterpret_cast(&val) }; + const std::uint16_t* last { first + std::size_t { sizeof(float_type) / sizeof(std::uint16_t) } }; while (first != last) { diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 951204684..9cb3ba254 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -175,6 +175,7 @@ test-suite arithmetic_tests : [ run test_arithmetic_ab_3.cpp no_eh_support : : : [ check-target-builds ../config//has_float128 : quadmath ] ] [ run test_cpp_dec_float_round.cpp no_eh_support ] + [ run test_various_edges.cpp no_eh_support ] [ run test_arithmetic_logged_1.cpp no_eh_support : : : msvc:-bigobj [ check-target-builds ../config//has_float128 : quadmath ] ] [ run test_arithmetic_logged_2.cpp no_eh_support : : : msvc:-bigobj [ check-target-builds ../config//has_float128 : quadmath ] ] diff --git a/test/test_arithmetic.hpp b/test/test_arithmetic.hpp index 6bc741aa9..6b25b4cf6 100644 --- a/test/test_arithmetic.hpp +++ b/test/test_arithmetic.hpp @@ -1736,6 +1736,25 @@ void test_float_ops(const std::integral_constant::is_specialized && std::numeric_limits::has_infinity && std::numeric_limits::max_exponent10 > 18 && std::numeric_limits::min_exponent10 < -18) + { + Real a = (std::numeric_limits::max)(); + + a /= 1000000; + a /= 1000000; + a /= 1000000; + + BOOST_CHECK((boost::math::isfinite)(a)); + + a = (std::numeric_limits::min)(); + + a *= 1000000; + a *= 1000000; + a *= 1000000; + + BOOST_CHECK((boost::math::isfinite)(a)); + } + test_float_funcs(std::integral_constant::is_specialized>()); } diff --git a/test/test_exp.cpp b/test/test_exp.cpp index a4ce87331..c2ebd16ff 100644 --- a/test/test_exp.cpp +++ b/test/test_exp.cpp @@ -18,6 +18,7 @@ #include "test.hpp" #include +#include #include #if !defined(TEST_MPF_50) && !defined(TEST_MPF) && !defined(TEST_BACKEND) && !defined(TEST_CPP_DEC_FLOAT) && !defined(TEST_MPFR) && !defined(TEST_MPFR_50) && !defined(TEST_MPFI_50) && !defined(TEST_FLOAT128) && !defined(TEST_CPP_BIN_FLOAT) && !defined(TEST_CPP_DOUBLE_FLOAT) @@ -228,95 +229,6 @@ void test() } } -template auto my_zero() -> FloatType&; -template auto my_one () -> FloatType&; - -template -auto test_exp_edge() -> bool -{ - using float_type = FloatType; - - std::mt19937_64 gen; - - std::uniform_real_distribution - dist - ( - static_cast(1.01L), - static_cast(1.04L) - ); - - auto result_is_ok = true; - - for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(4)); ++i) - { - static_cast(i); - - const float_type val_nan { exp(std::numeric_limits::quiet_NaN() * static_cast(dist(gen))) }; - - const bool result_val_nan_is_ok { isnan(val_nan) }; - - 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(4)); ++i) - { - static_cast(i); - - const float_type arg_inf { std::numeric_limits::infinity() * static_cast(dist(gen)) }; - - const float_type val_inf_pos { exp(arg_inf) }; - - const bool result_val_inf_pos_is_ok { (fpclassify(val_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(4)); ++i) - { - static_cast(i); - - const float_type val_inf_neg { exp(-std::numeric_limits::infinity() * static_cast(dist(gen))) }; - - const bool result_val_inf_neg_is_ok { (val_inf_neg == ::my_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(4)); ++i) - { - static_cast(i); - - const float_type val_zero_pos { exp(::my_zero()) }; - - const bool result_val_zero_pos_is_ok { (val_zero_pos == ::my_one()) }; - - BOOST_TEST(result_val_zero_pos_is_ok); - - result_is_ok = (result_val_zero_pos_is_ok && result_is_ok); - } - - for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(4)); ++i) - { - static_cast(i); - - const float_type val_zero_neg { exp(-::my_zero()) }; - - const bool result_val_zero_neg_is_ok { (val_zero_neg == ::my_one()) }; - - BOOST_TEST(result_val_zero_neg_is_ok); - - result_is_ok = (result_val_zero_neg_is_ok && result_is_ok); - } - - return result_is_ok; -} - int main() { #ifdef TEST_BACKEND @@ -368,16 +280,6 @@ int main() #if defined(BOOST_HAS_FLOAT128) test(); #endif - - test_exp_edge(); - test_exp_edge(); - test_exp_edge(); - #if defined(BOOST_HAS_FLOAT128) - test_exp_edge(); - #endif #endif return boost::report_errors(); } - -template auto my_zero() -> FloatType& { using float_type = FloatType; static float_type val_zero { 0 }; return val_zero; } -template auto my_one () -> FloatType& { using float_type = FloatType; static float_type val_one { 1 }; return val_one; } diff --git a/test/test_various_edges.cpp b/test/test_various_edges.cpp new file mode 100644 index 000000000..24aeca9e6 --- /dev/null +++ b/test/test_various_edges.cpp @@ -0,0 +1,245 @@ +// Copyright 2023 - 2024 Matt Borland +// Copyright 2023 - 2024 Christopher Kormanyos +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#include +#include +#include +#include +#include + +#include +#include +#include + +#if defined(__clang__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wfloat-equal" +#elif defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wfloat-equal" +#endif + +#include + +template auto my_zero() -> FloatType&; +template auto my_one () -> FloatType&; + +namespace local +{ + template + auto time_point() noexcept -> IntegralTimePointType + { + using local_integral_time_point_type = IntegralTimePointType; + using local_clock_type = ClockType; + + const auto current_now = + static_cast + ( + std::chrono::duration_cast + ( + local_clock_type::now().time_since_epoch() + ).count() + ); + + return static_cast(current_now); + } + + 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_edges() + { + using float_type = FloatType; + using ctrl_type = boost::multiprecision::number, boost::multiprecision::et_off>; + + std::mt19937_64 gen; + + gen.seed(time_point()); + + auto dis = + std::uniform_real_distribution + { + static_cast(1.01F), + static_cast(1.04F) + }; + + bool result_is_ok { true }; + + { + float_type flt_val { (std::numeric_limits::max)() }; + ctrl_type ctl_val { flt_val }; + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) + { + static_cast(i); + + float_type flt_denom { 1000.0F * dis(gen) }; + + flt_val /= flt_denom; + ctl_val /= ctrl_type { flt_denom }; + + const bool result_div_is_ok { local::is_close_fraction(flt_val, float_type { ctl_val }, std::numeric_limits::epsilon() * 32) }; + + BOOST_TEST(result_div_is_ok); + + result_is_ok = (result_div_is_ok && result_is_ok); + } + } + + { + float_type flt_val { (std::numeric_limits::min)() }; + ctrl_type ctl_val { flt_val }; + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) + { + static_cast(i); + + float_type flt_factor { 1000.0F * dis(gen) }; + + flt_val *= flt_factor; + ctl_val *= ctrl_type { flt_factor }; + + const bool result_mul_is_ok { local::is_close_fraction(flt_val, float_type { ctl_val }, std::numeric_limits::epsilon() * 32) }; + + BOOST_TEST(result_mul_is_ok); + + result_is_ok = (result_mul_is_ok && result_is_ok); + } + } + + return result_is_ok; + } + + template + auto test_exp_edge() -> bool + { + using float_type = FloatType; + + std::mt19937_64 gen(static_cast(std::clock())); + + std::uniform_real_distribution + dist + ( + static_cast(1.01L), + static_cast(1.04L) + ); + + auto result_is_ok = true; + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) + { + static_cast(i); + + const float_type val_nan { exp(std::numeric_limits::quiet_NaN() * static_cast(dist(gen))) }; + + const bool result_val_nan_is_ok { isnan(val_nan) }; + + 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(16)); ++i) + { + static_cast(i); + + const float_type arg_inf { std::numeric_limits::infinity() * static_cast(dist(gen)) }; + + const float_type val_inf_pos { exp(arg_inf) }; + + const bool result_val_inf_pos_is_ok { (fpclassify(val_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(16)); ++i) + { + static_cast(i); + + const float_type val_inf_neg { exp(-std::numeric_limits::infinity() * static_cast(dist(gen))) }; + + const bool result_val_inf_neg_is_ok { (val_inf_neg == ::my_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(16)); ++i) + { + static_cast(i); + + const float_type val_zero { exp(::my_zero() * static_cast(dist(gen))) }; + + const bool result_val_zero_is_ok { (val_zero == ::my_one()) }; + + BOOST_TEST(result_val_zero_is_ok); + + result_is_ok = (result_val_zero_is_ok && result_is_ok); + } + + return result_is_ok; + } + +} // namespace local + +auto main() -> int +{ + { + using float_type = boost::multiprecision::cpp_double_float; + + static_cast(local::test_edges()); + local::test_exp_edge(); + } + + { + using float_type = boost::multiprecision::cpp_double_double; + + static_cast(local::test_edges()); + local::test_exp_edge(); + } + + { + using float_backend_type = boost::multiprecision::cpp_bin_float<106, boost::multiprecision::digit_base_2, void, int, -1021, +1024>; + + using float_type = boost::multiprecision::number; + + static_cast(local::test_edges()); + local::test_exp_edge(); + } + + return boost::report_errors(); +} + +template auto my_zero() -> FloatType& { using float_type = FloatType; static float_type val_zero { 0 }; return val_zero; } +template auto my_one () -> FloatType& { using float_type = FloatType; static float_type val_one { 1 }; return val_one; } From a26326704b704c7a8df3273c91a752fe612e787c Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Sun, 12 Jan 2025 19:00:24 +0100 Subject: [PATCH 04/16] Remove call edge-func that's been removed --- test/test_exp.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/test_exp.cpp b/test/test_exp.cpp index c2ebd16ff..ea6394859 100644 --- a/test/test_exp.cpp +++ b/test/test_exp.cpp @@ -249,7 +249,6 @@ int main() #ifdef TEST_CPP_DEC_FLOAT test(); test(); - test_exp_edge(); #ifndef SLOW_COMPILER // Some "peculiar" digit counts which stress our code: test > >(); @@ -271,7 +270,6 @@ int main() #ifdef TEST_CPP_BIN_FLOAT test(); test, long long> > >(); - test_exp_edge(); #endif #ifdef TEST_CPP_DOUBLE_FLOAT test(); From 3c45826ccea2e1c7dcc6e707a410b38a4e7f4f1a Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Sun, 12 Jan 2025 20:35:51 +0100 Subject: [PATCH 05/16] Try more coverage and update license dates --- .../cpp_df_qf/cpp_df_qf_detail.hpp | 2 +- .../cpp_df_qf/cpp_df_qf_detail_ccmath.hpp | 2 +- .../cpp_df_qf_detail_ccmath_fabs.hpp | 2 +- .../cpp_df_qf_detail_ccmath_floor.hpp | 2 +- .../cpp_df_qf/cpp_df_qf_detail_ccmath_fma.hpp | 2 +- .../cpp_df_qf_detail_ccmath_fpclassify.hpp | 2 +- .../cpp_df_qf_detail_ccmath_frexp.hpp | 2 +- .../cpp_df_qf_detail_ccmath_isinf.hpp | 2 +- .../cpp_df_qf_detail_ccmath_isnan.hpp | 2 +- .../cpp_df_qf_detail_ccmath_ldexp.hpp | 2 +- .../cpp_df_qf_detail_ccmath_limits.hpp | 2 +- .../cpp_df_qf/cpp_df_qf_detail_ccmath_log.hpp | 2 +- .../cpp_df_qf_detail_ccmath_sqrt.hpp | 2 +- .../boost/multiprecision/cpp_double_fp.hpp | 2 +- test/test_various_edges.cpp | 92 +++++++++++++++++-- 15 files changed, 100 insertions(+), 20 deletions(-) diff --git a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail.hpp b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail.hpp index 67f8657a8..337f7f51e 100644 --- a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail.hpp +++ b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail.hpp @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// // Copyright 2021 Fahad Syed. -// Copyright 2021 - 2024 Christopher Kormanyos. +// Copyright 2021 - 2025 Christopher Kormanyos. // Copyright 2021 Janek Kozicki. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at diff --git a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath.hpp b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath.hpp index 74f893a1f..e5e05e56c 100644 --- a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath.hpp +++ b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath.hpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Copyright Christopher Kormanyos 2023. +// Copyright Christopher Kormanyos 2023 - 2025. // 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) diff --git a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_fabs.hpp b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_fabs.hpp index 8785efa16..83dcedb8f 100644 --- a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_fabs.hpp +++ b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_fabs.hpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Copyright Christopher Kormanyos 2023 - 2024. +// Copyright Christopher Kormanyos 2023 - 2025. // 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) diff --git a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_floor.hpp b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_floor.hpp index ae8bfbc8d..4df2f46a3 100644 --- a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_floor.hpp +++ b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_floor.hpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Copyright Christopher Kormanyos 2024. +// Copyright Christopher Kormanyos 2024 - 2025. // 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) diff --git a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_fma.hpp b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_fma.hpp index c9100f9f8..75bee6004 100644 --- a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_fma.hpp +++ b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_fma.hpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Copyright Christopher Kormanyos 2024. +// Copyright Christopher Kormanyos 2024 - 2025. // 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) diff --git a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_fpclassify.hpp b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_fpclassify.hpp index 3de62126c..7824e99ad 100644 --- a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_fpclassify.hpp +++ b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_fpclassify.hpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Copyright Christopher Kormanyos 2024. +// Copyright Christopher Kormanyos 2024 - 2025. // 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) diff --git a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_frexp.hpp b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_frexp.hpp index 3a5f89c8e..2ca53f32c 100644 --- a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_frexp.hpp +++ b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_frexp.hpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Copyright Christopher Kormanyos 2023 - 2024. +// Copyright Christopher Kormanyos 2023 - 2025. // 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) diff --git a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_isinf.hpp b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_isinf.hpp index 680de5397..7362c7f06 100644 --- a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_isinf.hpp +++ b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_isinf.hpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Copyright Christopher Kormanyos 2023 - 2024. +// Copyright Christopher Kormanyos 2023 - 2025. // 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) diff --git a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_isnan.hpp b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_isnan.hpp index 63fc3b468..468a97b97 100644 --- a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_isnan.hpp +++ b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_isnan.hpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Copyright Christopher Kormanyos 2023 - 2024. +// Copyright Christopher Kormanyos 2023 - 2025. // 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) diff --git a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_ldexp.hpp b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_ldexp.hpp index f9535d647..da2510969 100644 --- a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_ldexp.hpp +++ b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_ldexp.hpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Copyright Christopher Kormanyos 2023 - 2024. +// Copyright Christopher Kormanyos 2023 - 2025. // 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) 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 9b3e2d272..7d8b01c1f 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 @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Copyright Christopher Kormanyos 2023 - 2024. +// Copyright Christopher Kormanyos 2023 - 2025. // 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) diff --git a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_log.hpp b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_log.hpp index b9a8f42d9..64cd301ed 100644 --- a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_log.hpp +++ b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_log.hpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Copyright Christopher Kormanyos 2024. +// Copyright Christopher Kormanyos 2024 - 2025. // 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) diff --git a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_sqrt.hpp b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_sqrt.hpp index ba985d790..381570b91 100644 --- a/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_sqrt.hpp +++ b/include/boost/multiprecision/cpp_df_qf/cpp_df_qf_detail_ccmath_sqrt.hpp @@ -1,5 +1,5 @@ /////////////////////////////////////////////////////////////////////////////// -// Copyright Christopher Kormanyos 2023 - 2024. +// Copyright Christopher Kormanyos 2023 - 2025. // 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) diff --git a/include/boost/multiprecision/cpp_double_fp.hpp b/include/boost/multiprecision/cpp_double_fp.hpp index 82f785723..40673cab2 100644 --- a/include/boost/multiprecision/cpp_double_fp.hpp +++ b/include/boost/multiprecision/cpp_double_fp.hpp @@ -1,6 +1,6 @@ /////////////////////////////////////////////////////////////////////////////// // Copyright 2021 Fahad Syed. -// Copyright 2021 - 2024 Christopher Kormanyos. +// Copyright 2021 - 2025 Christopher Kormanyos. // Copyright 2021 Janek Kozicki. // Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at diff --git a/test/test_various_edges.cpp b/test/test_various_edges.cpp index 24aeca9e6..0ebff14a3 100644 --- a/test/test_various_edges.cpp +++ b/test/test_various_edges.cpp @@ -1,18 +1,20 @@ // Copyright 2023 - 2024 Matt Borland -// Copyright 2023 - 2024 Christopher Kormanyos +// Copyright 2023 - 2025 Christopher Kormanyos // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt +// Parts taken from the Boost.Decimal project + +#include +#include +#include + #include #include #include #include #include -#include -#include -#include - #if defined(__clang__) # pragma clang diagnostic push # pragma clang diagnostic ignored "-Wfloat-equal" @@ -138,12 +140,87 @@ namespace local return result_is_ok; } + template + auto test_sqrt_edge() -> bool + { + using float_type = FloatType; + + std::mt19937_64 gen; + + gen.seed(time_point()); + + std::uniform_real_distribution + dist + ( + static_cast(1.01L), + static_cast(1.04L) + ); + + auto result_is_ok = true; + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(4)); ++i) + { + static_cast(i); + + const auto val_nan = sqrt(-std::numeric_limits::quiet_NaN() * static_cast(dist(gen))); + + const auto result_val_nan_is_ok = isnan(val_nan); + + 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(4)); ++i) + { + static_cast(i); + + const auto val_inf_pos = sqrt(std::numeric_limits::infinity() * static_cast(dist(gen))); + + const auto result_val_inf_pos_is_ok = (isinf(val_inf_pos) && (!signbit(val_inf_pos))); + + 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(4)); ++i) + { + static_cast(i); + + const auto val_one = sqrt(::my_one()); + + const auto result_val_one_is_ok = (val_one == ::my_one()); + + BOOST_TEST(result_val_one_is_ok); + + result_is_ok = (result_val_one_is_ok && result_is_ok); + } + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(4)); ++i) + { + static_cast(i); + + const auto val_zero = sqrt(::my_zero()); + + const auto result_val_zero_is_ok = ((val_zero == ::my_zero()) && (!signbit(val_zero))); + + BOOST_TEST(result_val_zero_is_ok); + + result_is_ok = (result_val_zero_is_ok && result_is_ok); + } + + return result_is_ok; + } + template auto test_exp_edge() -> bool { using float_type = FloatType; - std::mt19937_64 gen(static_cast(std::clock())); + std::mt19937_64 gen; + + gen.seed(time_point()); std::uniform_real_distribution dist @@ -219,6 +296,7 @@ auto main() -> int using float_type = boost::multiprecision::cpp_double_float; static_cast(local::test_edges()); + local::test_sqrt_edge(); local::test_exp_edge(); } @@ -226,6 +304,7 @@ auto main() -> int using float_type = boost::multiprecision::cpp_double_double; static_cast(local::test_edges()); + local::test_sqrt_edge(); local::test_exp_edge(); } @@ -235,6 +314,7 @@ auto main() -> int using float_type = boost::multiprecision::number; static_cast(local::test_edges()); + local::test_sqrt_edge(); local::test_exp_edge(); } From 411b4844309dcd92c0eb421e3372894ec70446eb Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Mon, 13 Jan 2025 19:34:10 +0100 Subject: [PATCH 06/16] Update max-value and docs --- README.md | 40 ++++++------- doc/multiprecision.qbk | 8 +-- .../boost/multiprecision/cpp_double_fp.hpp | 56 +++++++++++++------ 3 files changed, 64 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index 2c0882963..17b0ecda0 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,14 @@ Boost Multiprecision Library | | Master | Develop | |------------------|----------|-------------| -| Drone | [![Build Status](https://drone.cpp.al/api/badges/boostorg/multiprecision/status.svg?ref=refs/heads/master)](https://drone.cpp.al/boostorg/multiprecision) | [![Build Status](https://drone.cpp.al/api/badges/boostorg/multiprecision/status.svg)](https://drone.cpp.al/boostorg/multiprecision) | -| Github Actions | [![Build Status](https://github.com/boostorg/multiprecision/workflows/multiprecision/badge.svg?branch=master)](https://github.com/boostorg/multiprecision/actions) | [![Build Status](https://github.com/boostorg/multiprecision/workflows/multiprecision/badge.svg?branch=develop)](https://github.com/boostorg/multiprecision/actions) | -| Codecov | [![codecov](https://codecov.io/gh/boostorg/multiprecision/branch/master/graph/badge.svg)](https://codecov.io/gh/boostorg/multiprecision/branch/master) | [![codecov](https://codecov.io/gh/boostorg/multiprecision/branch/develop/graph/badge.svg)](https://codecov.io/gh/boostorg/multiprecision/branch/develop) | +| Drone | [![Build Status](https://drone.cpp.al/api/badges/boostorg/multiprecision/status.svg?ref=refs/heads/master)](https://drone.cpp.al/boostorg/multiprecision) | [![Build Status](https://drone.cpp.al/api/badges/boostorg/multiprecision/status.svg)](https://drone.cpp.al/boostorg/multiprecision) | +| Github Actions | [![Build Status](https://github.com/boostorg/multiprecision/actions/workflows/multiprecision.yml/badge.svg?branch=master)](https://github.com/boostorg/multiprecision/actions) | [![Build Status](https://github.com/boostorg/multiprecision/actions/workflows/multiprecision.yml/badge.svg?branch=develop)](https://github.com/boostorg/multiprecision/actions) | +| Codecov | [![codecov](https://codecov.io/gh/boostorg/multiprecision/branch/master/graph/badge.svg)](https://codecov.io/gh/boostorg/multiprecision/branch/master) | [![codecov](https://codecov.io/gh/boostorg/multiprecision/branch/develop/graph/badge.svg)](https://codecov.io/gh/boostorg/multiprecision/branch/develop) | -`Boost.Multiprecision` is a C++ library that provides integer, rational, floating-point, complex and interval number types -having more range and precision than the language's ordinary built-in types. +`Boost.Multiprecision` is a C++ library that provides integer, rational, floating-point, +complex and interval number types having more range and precision than the language's +ordinary built-in types. Language adherence: - `Boost.Multiprecision` requires a compliant C++14 compiler. @@ -21,7 +22,7 @@ also interoperate with the built-in types in C++ using clearly defined conversio used for all kinds of mathematical calculations involving integer, rational and floating-point types requiring extended range and precision. Multiprecision consists of a generic interface to the mathematics of large numbers as well as a selection of big number back ends, with -support for integer, rational and floating-point types. `Boost.Multiprecision` provides a selection of back ends provided off-the-rack in +support for integer, rational and floating-point types. `Boost.Multiprecision` provides a selection of back ends provided off-the-rack including interfaces to GMP, MPFR, MPIR, TomMath as well as its own collection of Boost-licensed, header-only back ends for integers, rationals, floats and complex. In addition, user-defined back ends can be created and used with the interface of Multiprecision, provided the class implementation adheres to the necessary concepts. @@ -29,14 +30,14 @@ provided the class implementation adheres to the necessary concepts. Depending upon the number type, precision may be arbitrarily large (limited only by available memory), fixed at compile time (for example $50$ or $100$ decimal digits), or a variable controlled at run-time by member functions. The types are expression-template-enabled by default. This usually provides better performance than naive user-defined types. -If not needed, expression templates can be disabled when configuring the `number` type with its backend. +If not needed, expression templates can be disabled when configuring the `number`-type with its backend. The full documentation is available on [boost.org](http://www.boost.org/doc/libs/release/libs/multiprecision/index.html). -## Using Multiprecision ## +## Using Multiprecision

- +

@@ -49,20 +50,20 @@ where we also observe that Multiprecision can seemlesly interoperate with [Boost.Math](https://github.com/boostorg/math). ```cpp +#include +#include + #include #include #include -#include -#include - auto main() -> int { using big_float_type = boost::multiprecision::cpp_bin_float_100; const big_float_type sqrt_pi { sqrt(boost::math::constants::pi()) }; - const big_float_type half { big_float_type(1) / 2 }; + const big_float_type half { big_float_type { 1 } / 2 }; const big_float_type gamma_half { boost::math::tgamma(half) }; @@ -75,7 +76,7 @@ auto main() -> int } ``` -## Standalone ## +## Standalone Defining `BOOST_MP_STANDALONE` allows `Boost.Multiprecision` to be used with the only dependency being [Boost.Config](https://github.com/boostorg/config). @@ -83,11 +84,11 @@ to be used with the only dependency being [Boost.Config](https://github.com/boos Our [package on this page](https://github.com/boostorg/multiprecision/releases) already includes a copy of Boost.Config so no other downloads are required. Some functionality is reduced in this mode. -A static_assert message will alert you if a particular feature has been disabled by standalone mode. +A `static_assert` message will alert you if a particular feature has been disabled by standalone mode. [Boost.Math](https://github.com/boostorg/math) standalone mode is compatiable, -and recommended if special functions are required for the floating point types. +and recommended if special functions are required for the floating-point types. -## Support, bugs and feature requests ## +## Support, bugs and feature requests Bugs and feature requests can be reported through the [Gitub issue tracker](https://github.com/boostorg/multiprecision/issues) (see [open issues](https://github.com/boostorg/multiprecision/issues) and @@ -100,7 +101,7 @@ although you can use the general-purpose Boost [mailing-list](http://lists.boost using the tag [multiprecision]. -## Development ## +## Development Clone the whole boost project, which includes the individual Boost projects as submodules ([see boost+git doc](https://github.com/boostorg/boost/wiki/Getting-Started)): @@ -113,7 +114,8 @@ Clone the whole boost project, which includes the individual Boost projects as s The Boost Multiprecision Library is located in `libs/multiprecision/`. -### Running tests ### +### Running tests + First, build the `b2` engine by running `bootstrap.sh` in the root of the boost directory. This will generate `b2` configuration in `project-config.jam`. ```sh diff --git a/doc/multiprecision.qbk b/doc/multiprecision.qbk index 6a5072272..a8e9c2873 100644 --- a/doc/multiprecision.qbk +++ b/doc/multiprecision.qbk @@ -1,8 +1,8 @@ [/ - Copyright 2011 - 2024 John Maddock. + Copyright 2011 - 2025 John Maddock. Copyright 2013 - 2019 Paul A. Bristow. - Copyright 2013 - 2024 Christopher Kormanyos. - Copyright 2021 - 2024 Fahad Syed. + Copyright 2013 - 2025 Christopher Kormanyos. + Copyright 2021 - 2025 Fahad Syed. Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -11,7 +11,7 @@ [library Boost.Multiprecision [quickbook 1.7] - [copyright 2002-2020 John Maddock and Christopher Kormanyos] + [copyright 2002-2025 John Maddock and Christopher Kormanyos] [purpose Multiprecision Number library] [license Distributed under the Boost Software License, Version 1.0. diff --git a/include/boost/multiprecision/cpp_double_fp.hpp b/include/boost/multiprecision/cpp_double_fp.hpp index 40673cab2..173545b1d 100644 --- a/include/boost/multiprecision/cpp_double_fp.hpp +++ b/include/boost/multiprecision/cpp_double_fp.hpp @@ -986,33 +986,55 @@ class cpp_double_fp_backend // Use the non-normalized sum of two maximum values, where the lower // value is "shifted" right in the sense of floating-point ldexp. - // TBD: This value _still_ needs to be independently verified. + // The exact values used are: + // hi-part = (max_integral_val - 1) * 2^(max_exponent - numeric_limits::digits) + // + lo-part = + max_integral_val * 2^(max_exponent - 2*numeric_limits::digits) + + constexpr float_type + max_integral_val + { + cpp_df_qf_detail::ccmath::unsafe::ldexp((cpp_df_qf_detail::ccmath::numeric_limits::max)(), -my_max_exponent + cpp_df_qf_detail::ccmath::numeric_limits::digits) + }; + + constexpr float_type max_integral_val_minus_one { max_integral_val - float_type { 1.0F } }; + + constexpr float_type + max_scaled_hi + { + cpp_df_qf_detail::ccmath::unsafe::ldexp(max_integral_val_minus_one, -cpp_df_qf_detail::ccmath::numeric_limits::digits) + }; + + constexpr float_type + max_scaled_lo + { + cpp_df_qf_detail::ccmath::unsafe::ldexp(max_integral_val, -2 * cpp_df_qf_detail::ccmath::numeric_limits::digits) + }; + + constexpr cpp_double_fp_backend max_scaled { max_scaled_hi, max_scaled_lo }; constexpr cpp_double_fp_backend my_value_max_constexpr { - arithmetic::two_hilo_sum - ( - static_cast - ( - (cpp_df_qf_detail::ccmath::numeric_limits::max)() - * ( - static_cast(1.0F) - - static_cast(1.5F) * cpp_df_qf_detail::ccmath::unsafe::sqrt(cpp_df_qf_detail::ccmath::numeric_limits::epsilon()) - ) - ), - cpp_df_qf_detail::ccmath::unsafe::ldexp + cpp_double_fp_backend + { + cpp_double_fp_backend::arithmetic::normalize ( - (cpp_df_qf_detail::ccmath::numeric_limits::max)(), - -(cpp_df_qf_detail::ccmath::numeric_limits::digits + 1) + cpp_df_qf_detail::ccmath::unsafe::ldexp(max_scaled.data.first, my_max_exponent), + cpp_df_qf_detail::ccmath::unsafe::ldexp(max_scaled.data.second, my_max_exponent) ) - ) + } + }; + + constexpr cpp_double_fp_backend + my_value_max_lower_bound_constexpr + { + cpp_df_qf_detail::ccmath::unsafe::ldexp(max_integral_val_minus_one, my_max_exponent - cpp_df_qf_detail::ccmath::numeric_limits::digits) }; static_assert ( - eval_gt(my_value_max_constexpr, cpp_double_fp_backend { (cpp_df_qf_detail::ccmath::numeric_limits::max)() / 2 }), - "Error: maximum value is too small and must exceed (1/2 * max) of its constituent type" + eval_gt(my_value_max_constexpr, my_value_max_lower_bound_constexpr), + "Error: maximum value is too small in relation to maximum of its constituent type" ); return my_value_max_constexpr; From 47d56d246f499032727badf81df8a3f79b3c1a78 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Mon, 13 Jan 2025 20:04:41 +0100 Subject: [PATCH 07/16] Revert (almost) to original max-value --- .../boost/multiprecision/cpp_double_fp.hpp | 55 ++++++------------- 1 file changed, 16 insertions(+), 39 deletions(-) diff --git a/include/boost/multiprecision/cpp_double_fp.hpp b/include/boost/multiprecision/cpp_double_fp.hpp index 173545b1d..37b54f2ec 100644 --- a/include/boost/multiprecision/cpp_double_fp.hpp +++ b/include/boost/multiprecision/cpp_double_fp.hpp @@ -986,55 +986,32 @@ class cpp_double_fp_backend // Use the non-normalized sum of two maximum values, where the lower // value is "shifted" right in the sense of floating-point ldexp. - // The exact values used are: - // hi-part = (max_integral_val - 1) * 2^(max_exponent - numeric_limits::digits) - // + lo-part = + max_integral_val * 2^(max_exponent - 2*numeric_limits::digits) - - constexpr float_type - max_integral_val - { - cpp_df_qf_detail::ccmath::unsafe::ldexp((cpp_df_qf_detail::ccmath::numeric_limits::max)(), -my_max_exponent + cpp_df_qf_detail::ccmath::numeric_limits::digits) - }; - - constexpr float_type max_integral_val_minus_one { max_integral_val - float_type { 1.0F } }; - constexpr float_type - max_scaled_hi + hi_part { - cpp_df_qf_detail::ccmath::unsafe::ldexp(max_integral_val_minus_one, -cpp_df_qf_detail::ccmath::numeric_limits::digits) - }; + (cpp_df_qf_detail::ccmath::numeric_limits::max)() + * ( + static_cast(1.0F) + - static_cast(1.5F) * cpp_df_qf_detail::ccmath::unsafe::sqrt(cpp_df_qf_detail::ccmath::numeric_limits::epsilon()) + ) + }; constexpr float_type - max_scaled_lo - { - cpp_df_qf_detail::ccmath::unsafe::ldexp(max_integral_val, -2 * cpp_df_qf_detail::ccmath::numeric_limits::digits) - }; - - constexpr cpp_double_fp_backend max_scaled { max_scaled_hi, max_scaled_lo }; - - constexpr cpp_double_fp_backend - my_value_max_constexpr + lo_part { - cpp_double_fp_backend - { - cpp_double_fp_backend::arithmetic::normalize - ( - cpp_df_qf_detail::ccmath::unsafe::ldexp(max_scaled.data.first, my_max_exponent), - cpp_df_qf_detail::ccmath::unsafe::ldexp(max_scaled.data.second, my_max_exponent) - ) - } + cpp_df_qf_detail::ccmath::unsafe::ldexp + ( + (cpp_df_qf_detail::ccmath::numeric_limits::max)(), + -cpp_df_qf_detail::ccmath::numeric_limits::digits + ) }; - constexpr cpp_double_fp_backend - my_value_max_lower_bound_constexpr - { - cpp_df_qf_detail::ccmath::unsafe::ldexp(max_integral_val_minus_one, my_max_exponent - cpp_df_qf_detail::ccmath::numeric_limits::digits) - }; + constexpr cpp_double_fp_backend my_value_max_constexpr { arithmetic::two_hilo_sum(hi_part, lo_part) }; static_assert ( - eval_gt(my_value_max_constexpr, my_value_max_lower_bound_constexpr), - "Error: maximum value is too small in relation to maximum of its constituent type" + eval_gt(my_value_max_constexpr, cpp_double_fp_backend { hi_part }), + "Error: maximum value is too small in relation to the maximum of its constituent type" ); return my_value_max_constexpr; From e4fca21a2212a173027c6628e0188173161dd8f7 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Mon, 13 Jan 2025 21:15:51 +0100 Subject: [PATCH 08/16] More tests and syntax and docs --- README.md | 6 +- .../boost/multiprecision/cpp_double_fp.hpp | 2 +- test/test_various_edges.cpp | 184 +++++++++++++++++- 3 files changed, 184 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 17b0ecda0..38f5c5f3c 100644 --- a/README.md +++ b/README.md @@ -37,14 +37,16 @@ The full documentation is available on [boost.org](http://www.boost.org/doc/libs ## Using Multiprecision

- +

In the following example, we use Multiprecision's Boost-licensed binary floating-point backend type `cpp_bin_float` to compute ${\sim}100$ decimal digits of -$$\sqrt{\pi} = \Gamma \left( \frac{1}{2} \right)~{\approx}~1.772453850905516027298{\ldots}\text{,}$$ +$$ +\sqrt{\pi} = \Gamma \left( \frac{1}{2} \right)~{\approx}~1.772453850905516027298{\ldots}\text{,} +$$ where we also observe that Multiprecision can seemlesly interoperate with [Boost.Math](https://github.com/boostorg/math). diff --git a/include/boost/multiprecision/cpp_double_fp.hpp b/include/boost/multiprecision/cpp_double_fp.hpp index 37b54f2ec..9ee58feb9 100644 --- a/include/boost/multiprecision/cpp_double_fp.hpp +++ b/include/boost/multiprecision/cpp_double_fp.hpp @@ -1028,7 +1028,7 @@ class cpp_double_fp_backend cpp_df_qf_detail::ccmath::unsafe::ldexp ( (cpp_df_qf_detail::ccmath::numeric_limits::min)(), - cpp_df_qf_detail::ccmath::numeric_limits::digits + cpp_df_qf_detail::ccmath::numeric_limits::digits ) }; diff --git a/test/test_various_edges.cpp b/test/test_various_edges.cpp index 0ebff14a3..e066f8d2f 100644 --- a/test/test_various_edges.cpp +++ b/test/test_various_edges.cpp @@ -3,7 +3,7 @@ // Distributed under the Boost Software License, Version 1.0. // https://www.boost.org/LICENSE_1_0.txt -// Parts taken from the Boost.Decimal project +// Some parts of this test file have been taken from the Boost.Decimal project. #include #include @@ -25,8 +25,10 @@ #include -template auto my_zero() -> FloatType&; -template auto my_one () -> FloatType&; +template auto my_zero() noexcept -> FloatType&; +template auto my_one () noexcept -> FloatType&; +template auto my_inf () noexcept -> FloatType&; +template auto my_nan () noexcept -> FloatType&; namespace local { @@ -137,6 +139,53 @@ namespace local } } + { + const float_type inf_pos_01 { "1e100001" }; + const float_type inf_pos_02 { "1e100002" }; + const float_type inf_pos_03 { "1e100003" }; + const float_type inf_neg_01 { "-1e100001" }; + const float_type inf_neg_02 { "-1e100002" }; + const float_type inf_neg_03 { "-1e100003" }; + + const float_type zero_01 { "1e-100001" }; + const float_type zero_02 { "1e-100002" }; + const float_type zero_03 { "1e-100003" }; + + BOOST_TEST(result_is_ok = (isinf(inf_pos_01) && result_is_ok)); + BOOST_TEST(result_is_ok = (isinf(inf_pos_02) && result_is_ok)); + BOOST_TEST(result_is_ok = (isinf(inf_pos_03) && result_is_ok)); + + BOOST_TEST(result_is_ok = (isinf(inf_neg_01) && signbit(inf_neg_01) && result_is_ok)); + BOOST_TEST(result_is_ok = (isinf(inf_neg_02) && signbit(inf_neg_02) && result_is_ok)); + BOOST_TEST(result_is_ok = (isinf(inf_neg_03) && signbit(inf_neg_03) && result_is_ok)); + + BOOST_TEST(result_is_ok = ((fpclassify(zero_01) == FP_ZERO) && result_is_ok)); + BOOST_TEST(result_is_ok = ((fpclassify(zero_02) == FP_ZERO) && result_is_ok)); + BOOST_TEST(result_is_ok = ((fpclassify(zero_03) == FP_ZERO) && result_is_ok)); + } + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) + { + static_cast(i); + + float_type flt_x { 1000.0F + static_cast(i) * dis(gen) }; + + const float_type quotient_one = flt_x /= flt_x; + + BOOST_TEST(result_is_ok = ((quotient_one == 1) && result_is_ok)); + } + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) + { + static_cast(i); + + float_type flt_x { 1000.0F + static_cast(i) * dis(gen) }; + + const float_type sub_result_zero = flt_x -= flt_x; + + BOOST_TEST(result_is_ok = ((fpclassify(sub_result_zero) == FP_ZERO) && result_is_ok)); + } + return result_is_ok; } @@ -288,6 +337,126 @@ namespace local return result_is_ok; } + template + auto test_log_edge() -> bool + { + using float_type = FloatType; + + std::mt19937_64 gen; + + gen.seed(time_point()); + + std::uniform_real_distribution + dist + ( + static_cast(1.01L), + static_cast(1.04L) + ); + + auto result_is_ok = true; + + for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(8)); ++index) + { + static_cast(index); + + const float_type arg_zero = ::my_zero() * static_cast(dist(gen)); + + const auto log_zero = log(arg_zero); + + const volatile bool result_log_zero_is_ok + { + (fpclassify(arg_zero) == FP_ZERO) + && isinf(log_zero) + && signbit(log_zero) + }; + + BOOST_TEST(result_log_zero_is_ok); + + result_is_ok = (result_log_zero_is_ok && result_is_ok); + } + + for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(8)); ++index) + { + static_cast(index); + + float_type arg_one = + static_cast + ( + static_cast(::my_one() * static_cast(dist(gen))) + ); + + const auto log_one = log(arg_one); + + const volatile auto result_log_one_is_ok = ((arg_one == ::my_one()) && (log_one == ::my_zero())); + + BOOST_TEST(result_log_one_is_ok); + + result_is_ok = (result_log_one_is_ok && result_is_ok); + } + + for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(8)); ++index) + { + static_cast(index); + + float_type arg_one_minus = + static_cast + ( + -static_cast(::my_one() * static_cast(dist(gen))) + ); + + const auto log_one_minus = log(arg_one_minus); + + const volatile auto result_log_one_minus_is_ok = ((-arg_one_minus == ::my_one()) && isnan(log_one_minus)); + + BOOST_TEST(result_log_one_minus_is_ok); + + result_is_ok = (result_log_one_minus_is_ok && result_is_ok); + } + + for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(8)); ++index) + { + static_cast(index); + + const auto log_inf = log(::my_inf() * static_cast(dist(gen))); + + const volatile auto result_log_inf_is_ok = isinf(log_inf); + + BOOST_TEST(result_log_inf_is_ok); + + result_is_ok = (result_log_inf_is_ok && result_is_ok); + } + + for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(8)); ++index) + { + static_cast(index); + + const auto log_inf_minus = log(-::my_inf() * static_cast(dist(gen))); + + const volatile auto result_log_inf_minus_is_ok = isnan(log_inf_minus); + + BOOST_TEST(result_log_inf_minus_is_ok); + + result_is_ok = (result_log_inf_minus_is_ok && result_is_ok); + } + + for(auto index = static_cast(UINT8_C(0)); index < static_cast(UINT8_C(8)); ++index) + { + static_cast(index); + + const float_type arg_nan = ::my_nan() * static_cast(dist(gen)); + + const float_type log_nan = log(arg_nan); + + const volatile auto result_log_nan_is_ok = (isnan(arg_nan) && isnan(log_nan)); + + BOOST_TEST(result_log_nan_is_ok); + + result_is_ok = (result_log_nan_is_ok && result_is_ok); + } + + return result_is_ok; + } + } // namespace local auto main() -> int @@ -298,6 +467,7 @@ auto main() -> int static_cast(local::test_edges()); local::test_sqrt_edge(); local::test_exp_edge(); + local::test_log_edge(); } { @@ -306,6 +476,7 @@ auto main() -> int static_cast(local::test_edges()); local::test_sqrt_edge(); local::test_exp_edge(); + local::test_log_edge(); } { @@ -316,10 +487,13 @@ auto main() -> int static_cast(local::test_edges()); local::test_sqrt_edge(); local::test_exp_edge(); + local::test_log_edge(); } return boost::report_errors(); } -template auto my_zero() -> FloatType& { using float_type = FloatType; static float_type val_zero { 0 }; return val_zero; } -template auto my_one () -> FloatType& { using float_type = FloatType; static float_type val_one { 1 }; return val_one; } +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 my_inf_val { std::numeric_limits::infinity() }; return my_inf_val; } +template auto my_nan () noexcept -> FloatType& { using float_type = FloatType; static float_type my_inf_nan { std::numeric_limits::quiet_NaN() }; return my_inf_nan; } From 839b9b792a10bd6656ff2e117038568438eeafcc Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Mon, 13 Jan 2025 22:39:53 +0100 Subject: [PATCH 09/16] Repair some defects found in coverage runs --- .../boost/multiprecision/cpp_double_fp.hpp | 40 +++++-------------- test/test_various_edges.cpp | 30 ++++++++++++++ 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/include/boost/multiprecision/cpp_double_fp.hpp b/include/boost/multiprecision/cpp_double_fp.hpp index 9ee58feb9..af740f387 100644 --- a/include/boost/multiprecision/cpp_double_fp.hpp +++ b/include/boost/multiprecision/cpp_double_fp.hpp @@ -688,14 +688,6 @@ class cpp_double_fp_backend const bool iszero_u { (fpc_u == FP_ZERO) }; const bool iszero_v { (fpc_v == FP_ZERO) }; - if (this == &v) - { - data.first = float_type { 1.0F }; - data.second = float_type { 0.0F }; - - return *this; - } - if (iszero_u) { if (iszero_v) @@ -746,6 +738,14 @@ class cpp_double_fp_backend } } + if (this == &v) + { + data.first = float_type { 1.0F }; + data.second = float_type { 0.0F }; + + return *this; + } + // The division 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 @@ -1474,8 +1474,6 @@ constexpr void eval_exp(cpp_double_fp_backend& result, const { const int fpc { eval_fpclassify(x) }; - const bool x_is_zero { fpc == FP_ZERO }; - using double_float_type = cpp_double_fp_backend; if (fpc == FP_ZERO) @@ -1529,11 +1527,7 @@ constexpr void eval_exp(cpp_double_fp_backend& result, const }() }; - if (x_is_zero) - { - result = double_float_type(1U); - } - else if (eval_lt(x, min_exp_input)) + if (eval_lt(x, min_exp_input)) { result = double_float_type(0U); } @@ -1628,8 +1622,6 @@ constexpr void eval_exp(cpp_double_fp_backend& result, const { const int fpc { eval_fpclassify(x) }; - const bool x_is_zero { fpc == FP_ZERO }; - using double_float_type = cpp_double_fp_backend; if (fpc == FP_ZERO) @@ -1683,11 +1675,7 @@ constexpr void eval_exp(cpp_double_fp_backend& result, const }() }; - if (x_is_zero) - { - result = double_float_type(1U); - } - else if (eval_lt(x, min_exp_input)) + if (eval_lt(x, min_exp_input)) { result = double_float_type(0U); } @@ -1782,8 +1770,6 @@ constexpr void eval_exp(cpp_double_fp_backend& result, const { const int fpc { eval_fpclassify(x) }; - const bool x_is_zero { fpc == FP_ZERO }; - using double_float_type = cpp_double_fp_backend; if (fpc == FP_ZERO) @@ -1837,11 +1823,7 @@ constexpr void eval_exp(cpp_double_fp_backend& result, const }() }; - if (x_is_zero) - { - result = double_float_type(1U); - } - else if (eval_lt(x, min_exp_input)) + if (eval_lt(x, min_exp_input)) { result = double_float_type(0U); } diff --git a/test/test_various_edges.cpp b/test/test_various_edges.cpp index e066f8d2f..f102bdd25 100644 --- a/test/test_various_edges.cpp +++ b/test/test_various_edges.cpp @@ -334,6 +334,36 @@ namespace local result_is_ok = (result_val_zero_is_ok && result_is_ok); } + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) + { + static_cast(i); + + const float_type arg_large { ldexp(float_type { 3.14F } * static_cast(dist(gen)), static_cast(static_cast(std::numeric_limits::max_exponent) * 0.8)) }; + + const float_type result_exp_large { exp(arg_large) }; + + const bool result_exp_large_is_ok { isinf(result_exp_large) }; + + BOOST_TEST(result_exp_large_is_ok); + + result_is_ok = (result_exp_large_is_ok && result_is_ok); + } + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) + { + static_cast(i); + + const float_type arg_small { ldexp(float_type { -3.14F } * static_cast(dist(gen)), static_cast(static_cast(std::numeric_limits::max_exponent) * 0.8)) }; + + const float_type result_exp_small { exp(arg_small) }; + + const bool result_exp_small_is_ok { (fpclassify(result_exp_small) == FP_ZERO) }; + + BOOST_TEST(result_exp_small_is_ok); + + result_is_ok = (result_exp_small_is_ok && result_is_ok); + } + return result_is_ok; } From cac572509e2c99842c3c9bb3711c034fb05feeac Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Tue, 14 Jan 2025 18:26:59 +0100 Subject: [PATCH 10/16] Try improve cover and remove unused code --- .../boost/multiprecision/cpp_double_fp.hpp | 101 +----- test/test_various_edges.cpp | 324 +++++++++++++++++- 2 files changed, 332 insertions(+), 93 deletions(-) diff --git a/include/boost/multiprecision/cpp_double_fp.hpp b/include/boost/multiprecision/cpp_double_fp.hpp index af740f387..e120def03 100644 --- a/include/boost/multiprecision/cpp_double_fp.hpp +++ b/include/boost/multiprecision/cpp_double_fp.hpp @@ -582,13 +582,7 @@ class cpp_double_fp_backend if (iszero_u || iszero_v) { - const bool b_neg { (isneg_unchecked() != v.isneg_unchecked()) }; - - operator=(cpp_double_fp_backend(0)); - - if (b_neg) { negate(); } - - return *this; + return operator=(cpp_double_fp_backend(0)); } } @@ -850,56 +844,6 @@ class cpp_double_fp_backend return v; } - // Helper functions. - constexpr static cpp_double_fp_backend pown(const cpp_double_fp_backend& x, int p) - { - using local_float_type = cpp_double_fp_backend; - - local_float_type result { }; - - if (p < 0) - result = pown(local_float_type(1U) / x, -p); - else if (p == 0) - result = local_float_type(1U); - else if (p == 1) - result = x; - else if (p == 2) - result = local_float_type(x * x); - else if (p == 3) - result = local_float_type((x * x) * x); - else if (p == 4) - { const local_float_type x2 { x * x }; result = x2 * x2; } - else - { - result = local_float_type(1U); - - local_float_type y(x); - - auto p_local = static_cast(p); - - for (;;) - { - if (static_cast(p_local & static_cast(UINT8_C(1))) != static_cast(UINT8_C(0))) - { - result *= y; - } - - p_local >>= 1U; - - if (p_local == static_cast(UINT8_C(0))) - { - break; - } - else - { - y *= y; - } - } - } - - return result; - } - constexpr void swap(cpp_double_fp_backend& other) { if (this != &other) @@ -963,24 +907,6 @@ class cpp_double_fp_backend return e2; } - constexpr int order10() const { return static_cast(static_cast(order02()) * 0.301F); } - - constexpr bool small_arg() const { return (order10() < static_cast(-my_digits10 / 6)); } - - constexpr bool near_one() const - { - cpp_double_fp_backend delta_one { }; - - eval_subtract(delta_one, cpp_double_fp_backend(1U), *this); - - if (delta_one().isneg_unchecked()) - { - delta_one.negate(); - } - - return delta_one.small_arg(); - } - static constexpr cpp_double_fp_backend my_value_max() noexcept { // Use the non-normalized sum of two maximum values, where the lower @@ -1990,19 +1916,23 @@ constexpr void eval_convert_to(signed long long* result, const cpp_double_fp_bac return; } - constexpr signed long long my_max_val = (std::numeric_limits::max)(); - constexpr signed long long my_min_val = (std::numeric_limits::min)(); + constexpr signed long long my_max_val { (std::numeric_limits::max)() }; + constexpr signed long long my_min_val { (std::numeric_limits::min)() }; using c_type = typename std::common_type::type; - constexpr c_type my_max = static_cast(my_max_val); - const c_type ct = cpp_df_qf_detail::ccmath::fabs(backend.crep().first); + constexpr c_type my_max { static_cast(my_max_val) }; + constexpr c_type my_min { static_cast(my_min_val) }; + + const c_type ct { static_cast(backend.crep().first) }; if (ct > my_max) { - *result = backend.crep().first >= typename cpp_double_fp_backend::float_type(0U) - ? my_max_val - : my_min_val; + *result = my_max_val; + } + else if (ct < my_min) + { + *result = my_min_val; } else { @@ -2043,12 +1973,13 @@ constexpr void eval_convert_to(unsigned long long* result, const cpp_double_fp_b return; } - constexpr unsigned long long my_max_val = (std::numeric_limits::max)(); + constexpr unsigned long long my_max_val { (std::numeric_limits::max)() }; using c_type = typename std::common_type::type; - constexpr c_type my_max = static_cast(my_max_val); - const c_type ct = cpp_df_qf_detail::ccmath::fabs(backend.crep().first); + constexpr c_type my_max { static_cast(my_max_val) }; + + const c_type ct { static_cast(backend.crep().first) }; if (ct > my_max) { diff --git a/test/test_various_edges.cpp b/test/test_various_edges.cpp index f102bdd25..5617605c8 100644 --- a/test/test_various_edges.cpp +++ b/test/test_various_edges.cpp @@ -29,6 +29,7 @@ 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_exp1() noexcept -> FloatType&; namespace local { @@ -101,11 +102,11 @@ namespace local float_type flt_val { (std::numeric_limits::max)() }; ctrl_type ctl_val { flt_val }; - for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) { static_cast(i); - float_type flt_denom { 1000.0F * dis(gen) }; + float_type flt_denom { 2345.6F * dis(gen) }; flt_val /= flt_denom; ctl_val /= ctrl_type { flt_denom }; @@ -122,11 +123,11 @@ namespace local float_type flt_val { (std::numeric_limits::min)() }; ctrl_type ctl_val { flt_val }; - for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) { static_cast(i); - float_type flt_factor { 1000.0F * dis(gen) }; + float_type flt_factor { 2345.6F * dis(gen) }; flt_val *= flt_factor; ctl_val *= ctrl_type { flt_factor }; @@ -139,6 +140,82 @@ namespace local } } + { + float_type flt_val { (std::numeric_limits::max)() }; + ctrl_type ctl_val { flt_val }; + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) + { + static_cast(i); + + float_type flt_factor { 1234.56e-12F * dis(gen) }; + + flt_val *= flt_factor; + ctl_val *= ctrl_type { flt_factor }; + + const bool result_mul_is_ok { local::is_close_fraction(flt_val, float_type { ctl_val }, std::numeric_limits::epsilon() * 32) }; + + BOOST_TEST(result_mul_is_ok); + + result_is_ok = (result_mul_is_ok && result_is_ok); + } + } + + { + float_type flt_val { }; + ctrl_type ctl_val { }; + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) + { + static_cast(i); + + flt_val = sqrt((std::numeric_limits::max)()); + ctl_val = ctrl_type { flt_val }; + + float_type flt_factor { 123.456F * dis(gen) }; + + unsigned j { static_cast(UINT8_C(0)) }; + + for(; j < static_cast(UINT16_C(8192)); ++j) + { + if(unsigned { j % unsigned { UINT8_C(3) } } == unsigned { UINT8_C(0)}) + { + flt_val = -flt_val; + ctl_val = -ctl_val; + } + + if(unsigned { j % unsigned { UINT8_C(2) } } == unsigned { UINT8_C(0)}) + { + flt_factor = -flt_factor; + } + + flt_val *= flt_factor; + ctl_val *= ctrl_type { flt_factor }; + + if(isinf(flt_val)) { break; } + + const bool result_finite_mul_is_ok + { + local::is_close_fraction(flt_val, float_type { ctl_val }, std::numeric_limits::epsilon() * 32) + && (signbit(flt_val) == signbit(ctl_val)) + }; + + BOOST_TEST(result_finite_mul_is_ok); + } + + const bool result_mul_is_ok + { + isinf(flt_val) + && (j < unsigned { UINT16_C(8192) }) + && (signbit(flt_val) == signbit(ctl_val)) + }; + + BOOST_TEST(result_mul_is_ok); + + result_is_ok = (result_mul_is_ok && result_is_ok); + } + } + { const float_type inf_pos_01 { "1e100001" }; const float_type inf_pos_02 { "1e100002" }; @@ -168,7 +245,7 @@ namespace local { static_cast(i); - float_type flt_x { 1000.0F + static_cast(i) * dis(gen) }; + float_type flt_x { 2345.6F + static_cast(i) * dis(gen) }; const float_type quotient_one = flt_x /= flt_x; @@ -186,6 +263,60 @@ namespace local BOOST_TEST(result_is_ok = ((fpclassify(sub_result_zero) == FP_ZERO) && result_is_ok)); } + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) + { + static_cast(i); + + float_type flt_x { float_type { (std::numeric_limits::max)() } * static_cast((static_cast(i) + 1) * 2) * dis(gen) }; + + const signed long long conversion_result_max { static_cast(flt_x) }; + + BOOST_TEST(result_is_ok = ((conversion_result_max == (std::numeric_limits::max)()) && result_is_ok)); + } + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) + { + static_cast(i); + + float_type flt_x { float_type { (std::numeric_limits::min)() } * static_cast((static_cast(i) + 1) * 2) * dis(gen) }; + + const signed long long conversion_result_min { static_cast(flt_x) }; + + BOOST_TEST(result_is_ok = ((conversion_result_min == (std::numeric_limits::min)()) && result_is_ok)); + } + + return result_is_ok; + } + + template + bool test_edges_extra() + { + using float_type = FloatType; + + std::mt19937_64 gen; + + gen.seed(time_point()); + + auto dis = + std::uniform_real_distribution + { + static_cast(1.01F), + static_cast(1.04F) + }; + + bool result_is_ok { true }; + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) + { + static_cast(i); + + float_type flt_x { float_type { (std::numeric_limits::max)() } * static_cast((static_cast(i) + 1) * 2) * dis(gen) }; + + const unsigned long long conversion_result_max { static_cast(flt_x) }; + + BOOST_TEST(result_is_ok = ((conversion_result_max == (std::numeric_limits::max)()) && result_is_ok)); + } + return result_is_ok; } @@ -275,7 +406,7 @@ namespace local dist ( static_cast(1.01L), - static_cast(1.04L) + static_cast(1.08L) ); auto result_is_ok = true; @@ -364,6 +495,170 @@ namespace local result_is_ok = (result_exp_small_is_ok && result_is_ok); } + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) + { + static_cast(i); + + const float_type one { 1 }; + + const float_type + arg_near_one + { + one + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + }; + + using ctrl_type = boost::multiprecision::number, boost::multiprecision::et_off>; + + const float_type result_exp_n { exp(float_type { static_cast(arg_near_one) }) }; + const ctrl_type result_ctrl { exp(ctrl_type { static_cast(arg_near_one) }) }; + + bool result_exp_n_is_ok + { + local::is_close_fraction + ( + result_exp_n, + float_type { result_ctrl }, + std::numeric_limits::epsilon() * 512 + ) + }; + + if(static_cast(arg_near_one) == 1) + { + const bool result_exp_n_at_one_is_ok + { + local::is_close_fraction + ( + result_exp_n, + ::my_exp1(), + std::numeric_limits::epsilon() * 16 + ) + }; + + result_exp_n_is_ok = (result_exp_n_at_one_is_ok && result_exp_n_is_ok); + } + + BOOST_TEST(result_exp_n_is_ok); + + result_is_ok = (result_exp_n_is_ok && result_is_ok); + } + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) + { + static_cast(i); + + const float_type one { 1 }; + + const float_type + arg_near_one + { + one + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + }; + + using ctrl_type = boost::multiprecision::number, boost::multiprecision::et_off>; + + const float_type result_exp_n { exp(float_type { static_cast(-arg_near_one) }) }; + const ctrl_type result_ctrl { exp(ctrl_type { static_cast(-arg_near_one) }) }; + + bool result_exp_n_is_ok + { + local::is_close_fraction + ( + result_exp_n, + float_type { result_ctrl }, + std::numeric_limits::epsilon() * 512 + ) + }; + + if(static_cast(-arg_near_one) == -1) + { + const bool result_exp_n_at_one_is_ok + { + local::is_close_fraction + ( + result_exp_n, + 1 / ::my_exp1(), + std::numeric_limits::epsilon() * 16 + ) + }; + + result_exp_n_is_ok = (result_exp_n_at_one_is_ok && result_exp_n_is_ok); + } + + BOOST_TEST(result_exp_n_is_ok); + + result_is_ok = (result_exp_n_is_ok && result_is_ok); + } + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) + { + static_cast(i); + + const float_type arg_huge { sqrt((std::numeric_limits::max)()) * static_cast(dist(gen)) }; + + const float_type result_exp_huge { exp(arg_huge) }; + + const bool result_exp_huge_is_ok { (fpclassify(result_exp_huge) == FP_INFINITE) }; + + BOOST_TEST(result_exp_huge_is_ok); + + result_is_ok = (result_exp_huge_is_ok && result_is_ok); + } + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) + { + static_cast(i); + + const float_type arg_tiny { -sqrt((std::numeric_limits::max)()) * static_cast(dist(gen)) }; + + const float_type result_exp_tiny { exp(arg_tiny) }; + + const bool result_exp_tiny_is_ok { (fpclassify(result_exp_tiny) == FP_ZERO) }; + + BOOST_TEST(result_exp_tiny_is_ok); + + result_is_ok = (result_exp_tiny_is_ok && result_is_ok); + } + return result_is_ok; } @@ -495,6 +790,7 @@ auto main() -> int using float_type = boost::multiprecision::cpp_double_float; static_cast(local::test_edges()); + static_cast(local::test_edges_extra()); local::test_sqrt_edge(); local::test_exp_edge(); local::test_log_edge(); @@ -504,6 +800,17 @@ auto main() -> int using float_type = boost::multiprecision::cpp_double_double; static_cast(local::test_edges()); + static_cast(local::test_edges_extra()); + local::test_sqrt_edge(); + local::test_exp_edge(); + local::test_log_edge(); + } + + { + using float_type = boost::multiprecision::cpp_double_long_double; + + static_cast(local::test_edges()); + static_cast(local::test_edges_extra()); local::test_sqrt_edge(); local::test_exp_edge(); local::test_log_edge(); @@ -525,5 +832,6 @@ auto main() -> int 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 my_inf_val { std::numeric_limits::infinity() }; return my_inf_val; } -template auto my_nan () noexcept -> FloatType& { using float_type = FloatType; static float_type my_inf_nan { std::numeric_limits::quiet_NaN() }; return my_inf_nan; } +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 auto my_exp1() noexcept -> FloatType& { using float_type = FloatType; static float_type val_exp1 { "2.718281828459045235360287471352662497757247093699959574966967627724076630353547594571382178525166427427466391932003059921817413596629043572900334295260595630738132328627943490763233829880753195251019011573834187930702154089149934884167509244761460668082265" }; return val_exp1; } From 382cd8fc4f16f1d24f71798a14da3244f837ae22 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Tue, 14 Jan 2025 20:04:31 +0100 Subject: [PATCH 11/16] More tests and minor corrections --- .../boost/multiprecision/cpp_double_fp.hpp | 19 ++-- test/test_various_edges.cpp | 89 ++++++++++++++++--- 2 files changed, 90 insertions(+), 18 deletions(-) diff --git a/include/boost/multiprecision/cpp_double_fp.hpp b/include/boost/multiprecision/cpp_double_fp.hpp index e120def03..db6b2237c 100644 --- a/include/boost/multiprecision/cpp_double_fp.hpp +++ b/include/boost/multiprecision/cpp_double_fp.hpp @@ -2030,14 +2030,18 @@ constexpr void eval_convert_to(int128_type* result, const cpp_double_fp_backend< using c_type = typename std::common_type::type; - constexpr c_type my_max = static_cast(my_max_val); - const c_type ct = cpp_df_qf_detail::ccmath::fabs(backend.crep().first); + constexpr c_type my_max { static_cast(my_max_val) }; + constexpr c_type my_min { static_cast(my_min_val) }; + + const c_type ct { static_cast(backend.crep().first) }; if (ct > my_max) { - *result = backend.crep().first >= typename cpp_double_fp_backend::float_type(0U) - ? my_max_val - : my_min_val; + *result = my_max_val; + } + if (ct < my_min) + { + *result = my_min_val; } else { @@ -2091,8 +2095,9 @@ constexpr void eval_convert_to(uint128_type* result, const cpp_double_fp_backend using c_type = typename std::common_type::type; - const c_type my_max = static_cast(my_max_val); - const c_type ct = cpp_df_qf_detail::ccmath::fabs(backend.crep().first); + constexpr c_type my_max { static_cast(my_max_val) }; + + const c_type ct { static_cast(backend.crep().first) }; if (ct > my_max) { diff --git a/test/test_various_edges.cpp b/test/test_various_edges.cpp index 5617605c8..97d256673 100644 --- a/test/test_various_edges.cpp +++ b/test/test_various_edges.cpp @@ -85,9 +85,7 @@ namespace local using float_type = FloatType; using ctrl_type = boost::multiprecision::number, boost::multiprecision::et_off>; - std::mt19937_64 gen; - - gen.seed(time_point()); + std::mt19937_64 gen { time_point() }; auto dis = std::uniform_real_distribution @@ -285,6 +283,77 @@ namespace local BOOST_TEST(result_is_ok = ((conversion_result_min == (std::numeric_limits::min)()) && result_is_ok)); } + { + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) + { + static_cast(i); + + const float_type flt_factor_inf_pos { std::numeric_limits::infinity() * dis(gen) }; + const float_type flt_factor_inf_neg { std::numeric_limits::infinity() * -dis(gen) }; + + { + const float_type val_inf_pos_neg_add { flt_factor_inf_pos + flt_factor_inf_neg }; + + const bool result_inf_pos_neg_add_is_ok { isnan(val_inf_pos_neg_add) }; + + BOOST_TEST(result_inf_pos_neg_add_is_ok); + + result_is_ok = (result_inf_pos_neg_add_is_ok && result_is_ok); + } + + { + const float_type val_inf_pos_pos_add { flt_factor_inf_pos + -flt_factor_inf_neg }; + + const bool result_inf_pos_pos_add_is_ok { isinf(val_inf_pos_pos_add) }; + + BOOST_TEST(result_inf_pos_pos_add_is_ok); + + result_is_ok = (result_inf_pos_pos_add_is_ok && result_is_ok); + } + + { + const float_type val_inf_neg_neg_add { -flt_factor_inf_pos + (flt_factor_inf_neg) }; + + const bool result_inf_neg_neg_add_is_ok { isinf(val_inf_neg_neg_add) && signbit(val_inf_neg_neg_add) }; + + BOOST_TEST(result_inf_neg_neg_add_is_ok); + + result_is_ok = (result_inf_neg_neg_add_is_ok && result_is_ok); + } + + { + const float_type val_inf_pos_neg_sub { flt_factor_inf_pos - flt_factor_inf_neg }; + + const bool result_inf_pos_neg_sub_is_ok { isinf(val_inf_pos_neg_sub) }; + + BOOST_TEST(result_inf_pos_neg_sub_is_ok); + + result_is_ok = (result_inf_pos_neg_sub_is_ok && result_is_ok); + } + + { + const float_type val_inf_pos_pos_sub { flt_factor_inf_pos - (-flt_factor_inf_neg) }; + + const bool result_inf_pos_pos_sub_is_ok { isnan(val_inf_pos_pos_sub) }; + + BOOST_TEST(result_inf_pos_pos_sub_is_ok); + + result_is_ok = (result_inf_pos_pos_sub_is_ok && result_is_ok); + } + + { + const float_type val_inf_neg_neg_sub { -flt_factor_inf_pos - (flt_factor_inf_neg) }; + + const bool result_inf_neg_neg_sub_is_ok { isnan(val_inf_neg_neg_sub) }; + + BOOST_TEST(result_inf_neg_neg_sub_is_ok); + + result_is_ok = (result_inf_neg_neg_sub_is_ok && result_is_ok); + } + + } + } + return result_is_ok; } @@ -293,9 +362,7 @@ namespace local { using float_type = FloatType; - std::mt19937_64 gen; - - gen.seed(time_point()); + std::mt19937_64 gen { time_point() }; auto dis = std::uniform_real_distribution @@ -325,9 +392,7 @@ namespace local { using float_type = FloatType; - std::mt19937_64 gen; - - gen.seed(time_point()); + std::mt19937_64 gen { time_point() }; std::uniform_real_distribution dist @@ -723,11 +788,13 @@ namespace local { static_cast(index); - float_type arg_one_minus = + float_type arg_one_minus + { static_cast ( -static_cast(::my_one() * static_cast(dist(gen))) - ); + ) + }; const auto log_one_minus = log(arg_one_minus); From 223dba5ee6c2324dead32932062332ac10831541 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Tue, 14 Jan 2025 20:29:15 +0100 Subject: [PATCH 12/16] Correct some 128-bit constexpr typos --- include/boost/multiprecision/cpp_double_fp.hpp | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/include/boost/multiprecision/cpp_double_fp.hpp b/include/boost/multiprecision/cpp_double_fp.hpp index db6b2237c..a1b6b4a43 100644 --- a/include/boost/multiprecision/cpp_double_fp.hpp +++ b/include/boost/multiprecision/cpp_double_fp.hpp @@ -2025,8 +2025,8 @@ constexpr void eval_convert_to(int128_type* result, const cpp_double_fp_backend< return; } - constexpr int128_type my_max_val = (((static_cast(1) << (sizeof(int128_type) * CHAR_BIT - 2)) - 1) << 1) + 1; - constexpr int128_type my_min_val = static_cast(-my_max_val - 1); + constexpr int128_type my_max_val { (std::numeric_limits::max)() }; + constexpr int128_type my_min_val { (std::numeric_limits::min)() }; using c_type = typename std::common_type::type; @@ -2082,16 +2082,7 @@ constexpr void eval_convert_to(uint128_type* result, const cpp_double_fp_backend return; } - uint128_type my_max_val { }; - - BOOST_IF_CONSTEXPR(std::is_same::value && (std::numeric_limits::digits == 24)) - { - my_max_val = static_cast(FLT_MAX); - } - else - { - my_max_val = static_cast(~static_cast(0)); - } + constexpr uint128_type my_max_val { (std::numeric_limits::max)() }; using c_type = typename std::common_type::type; From 2fb4a44e46bdf8e1fa088cf3ae6bcefbf43295ac Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Tue, 14 Jan 2025 21:44:50 +0100 Subject: [PATCH 13/16] Try work around MinGW limits problem --- include/boost/multiprecision/cpp_double_fp.hpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/include/boost/multiprecision/cpp_double_fp.hpp b/include/boost/multiprecision/cpp_double_fp.hpp index a1b6b4a43..01aba6b3e 100644 --- a/include/boost/multiprecision/cpp_double_fp.hpp +++ b/include/boost/multiprecision/cpp_double_fp.hpp @@ -2025,8 +2025,8 @@ constexpr void eval_convert_to(int128_type* result, const cpp_double_fp_backend< return; } - constexpr int128_type my_max_val { (std::numeric_limits::max)() }; - constexpr int128_type my_min_val { (std::numeric_limits::min)() }; + constexpr int128_type my_max_val = (((static_cast(1) << (sizeof(int128_type) * CHAR_BIT - 2)) - 1) << 1) + 1; + constexpr int128_type my_min_val = static_cast(-my_max_val - 1); using c_type = typename std::common_type::type; @@ -2082,7 +2082,12 @@ constexpr void eval_convert_to(uint128_type* result, const cpp_double_fp_backend return; } - constexpr uint128_type my_max_val { (std::numeric_limits::max)() }; + constexpr uint128_type my_max_val + { + (std::is_same::value && (cpp_df_qf_detail::ccmath::numeric_limits::digits == 24)) + ? static_cast(FLT_MAX) + : static_cast(~static_cast(0)) + }; using c_type = typename std::common_type::type; From 8dc96ef694fe794467b05b096c552661a4551ed6 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Tue, 14 Jan 2025 21:47:34 +0100 Subject: [PATCH 14/16] Annotate bogus uncovered line report --- test/test_various_edges.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_various_edges.cpp b/test/test_various_edges.cpp index 97d256673..a0a92f8d1 100644 --- a/test/test_various_edges.cpp +++ b/test/test_various_edges.cpp @@ -788,7 +788,7 @@ namespace local { static_cast(index); - float_type arg_one_minus + float_type arg_one_minus // LCOV_EXCL_LINE { static_cast ( From a8efc3a6b169be0840a21f7261ef384a19f16d12 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Wed, 15 Jan 2025 10:23:13 +0100 Subject: [PATCH 15/16] Try remove unused lines and more cover --- .../boost/multiprecision/cpp_double_fp.hpp | 96 ++++--------------- test/test_various_edges.cpp | 75 +++++++++++++-- 2 files changed, 88 insertions(+), 83 deletions(-) diff --git a/include/boost/multiprecision/cpp_double_fp.hpp b/include/boost/multiprecision/cpp_double_fp.hpp index 01aba6b3e..704953050 100644 --- a/include/boost/multiprecision/cpp_double_fp.hpp +++ b/include/boost/multiprecision/cpp_double_fp.hpp @@ -1987,48 +1987,28 @@ constexpr void eval_convert_to(unsigned long long* result, const cpp_double_fp_b } else { - BOOST_IF_CONSTEXPR(std::numeric_limits::digits >= cpp_df_qf_detail::ccmath::numeric_limits::digits) - { - *result = static_cast(backend.crep().first); - *result += static_cast(backend.crep().second); - } - else - { - cpp_double_fp_backend source = backend; - - *result = 0; - - for(auto digit_count = 0; - digit_count < cpp_double_fp_backend::my_digits; - digit_count += std::numeric_limits::digits) - { - const auto next = static_cast(source.crep().first); - - *result += next; - - eval_subtract(source, cpp_double_fp_backend(next)); - } - } + *result = static_cast(backend.crep().first); + *result += static_cast(backend.crep().second); } } #ifdef BOOST_HAS_INT128 template -constexpr void eval_convert_to(int128_type* result, const cpp_double_fp_backend& backend) +constexpr void eval_convert_to(boost::int128_type* result, const cpp_double_fp_backend& backend) { const auto fpc = eval_fpclassify(backend); if (fpc != FP_NORMAL) { - *result = static_cast(backend.crep().first); + *result = static_cast(backend.crep().first); return; } - constexpr int128_type my_max_val = (((static_cast(1) << (sizeof(int128_type) * CHAR_BIT - 2)) - 1) << 1) + 1; - constexpr int128_type my_min_val = static_cast(-my_max_val - 1); + constexpr boost::int128_type my_max_val = (((static_cast(1) << (sizeof(boost::int128_type) * CHAR_BIT - 2)) - 1) << 1) + 1; + constexpr boost::int128_type my_min_val = static_cast(-my_max_val - 1); - using c_type = typename std::common_type::type; + using c_type = typename std::common_type::type; constexpr c_type my_max { static_cast(my_max_val) }; constexpr c_type my_min { static_cast(my_min_val) }; @@ -2045,51 +2025,31 @@ constexpr void eval_convert_to(int128_type* result, const cpp_double_fp_backend< } else { - BOOST_IF_CONSTEXPR(static_cast(static_cast(sizeof(int128_type)) * CHAR_BIT) >= cpp_df_qf_detail::ccmath::numeric_limits::digits) - { - *result = static_cast(backend.crep().first); - *result += static_cast(backend.crep().second); - } - else - { - cpp_double_fp_backend source = backend; - - *result = 0; - - for(auto digit_count = static_cast(0); - digit_count < cpp_double_fp_backend::my_digits; - digit_count += static_cast(static_cast(sizeof(int128_type)) * CHAR_BIT)) - { - const auto next = static_cast(source.crep().first); - - *result += next; - - eval_subtract(source, cpp_double_fp_backend(next)); - } - } + *result = static_cast(backend.crep().first); + *result += static_cast(backend.crep().second); } } template -constexpr void eval_convert_to(uint128_type* result, const cpp_double_fp_backend& backend) +constexpr void eval_convert_to(boost::uint128_type* result, const cpp_double_fp_backend& backend) { const auto fpc = eval_fpclassify(backend); if (fpc != FP_NORMAL) { - *result = static_cast(backend.crep().first); + *result = static_cast(backend.crep().first); return; } - constexpr uint128_type my_max_val + constexpr boost::uint128_type my_max_val { (std::is_same::value && (cpp_df_qf_detail::ccmath::numeric_limits::digits == 24)) - ? static_cast(FLT_MAX) - : static_cast(~static_cast(0)) + ? static_cast(FLT_MAX) + : static_cast(~static_cast(0)) }; - using c_type = typename std::common_type::type; + using c_type = typename std::common_type::type; constexpr c_type my_max { static_cast(my_max_val) }; @@ -2101,28 +2061,10 @@ constexpr void eval_convert_to(uint128_type* result, const cpp_double_fp_backend } else { - BOOST_IF_CONSTEXPR(static_cast(static_cast(sizeof(uint128_type)) * CHAR_BIT) >= cpp_df_qf_detail::ccmath::numeric_limits::digits) - { - *result = static_cast(backend.crep().first); - *result += static_cast(backend.crep().second); - } - else - { - cpp_double_fp_backend source = backend; - - *result = 0; - - for(auto digit_count = static_cast(0); - digit_count < cpp_double_fp_backend::my_digits; - digit_count += static_cast(static_cast(sizeof(uint128_type)) * CHAR_BIT)) - { - const auto next = static_cast(source.crep().first); - - *result += next; + *result = static_cast(backend.crep().first); + *result += static_cast(backend.crep().second); - eval_subtract(source, cpp_double_fp_backend(next)); - } - } + *result = static_cast(*result); } } #endif @@ -2133,7 +2075,7 @@ constexpr typename ::std::enable_if::max)() }; + ctrl_type ctl_val { flt_val }; + + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) + { + static_cast(i); + + float_type flt_factor { 1234.56e-12F * dis(gen) }; + + flt_val = flt_factor * flt_val; + ctl_val *= ctrl_type { flt_factor }; + + const bool result_mul_is_ok { local::is_close_fraction(flt_val, float_type { ctl_val }, std::numeric_limits::epsilon() * 32) }; + + BOOST_TEST(result_mul_is_ok); + + result_is_ok = (result_mul_is_ok && result_is_ok); + } + } + { float_type flt_val { }; ctrl_type ctl_val { }; @@ -222,9 +243,9 @@ namespace local const float_type inf_neg_02 { "-1e100002" }; const float_type inf_neg_03 { "-1e100003" }; - const float_type zero_01 { "1e-100001" }; - const float_type zero_02 { "1e-100002" }; - const float_type zero_03 { "1e-100003" }; + const float_type tiny_01 { "1e-100001" }; + const float_type tiny_02 { "1e-100002" }; + const float_type tiny_03 { "1e-100003" }; BOOST_TEST(result_is_ok = (isinf(inf_pos_01) && result_is_ok)); BOOST_TEST(result_is_ok = (isinf(inf_pos_02) && result_is_ok)); @@ -234,9 +255,9 @@ namespace local BOOST_TEST(result_is_ok = (isinf(inf_neg_02) && signbit(inf_neg_02) && result_is_ok)); BOOST_TEST(result_is_ok = (isinf(inf_neg_03) && signbit(inf_neg_03) && result_is_ok)); - BOOST_TEST(result_is_ok = ((fpclassify(zero_01) == FP_ZERO) && result_is_ok)); - BOOST_TEST(result_is_ok = ((fpclassify(zero_02) == FP_ZERO) && result_is_ok)); - BOOST_TEST(result_is_ok = ((fpclassify(zero_03) == FP_ZERO) && result_is_ok)); + BOOST_TEST(result_is_ok = ((fpclassify(tiny_01) == FP_ZERO) && result_is_ok)); + BOOST_TEST(result_is_ok = ((fpclassify(tiny_02) == FP_ZERO) && result_is_ok)); + BOOST_TEST(result_is_ok = ((fpclassify(tiny_03) == FP_ZERO) && result_is_ok)); } for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) @@ -261,6 +282,21 @@ namespace local BOOST_TEST(result_is_ok = ((fpclassify(sub_result_zero) == FP_ZERO) && result_is_ok)); } + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) + { + static_cast(i); + + float_type flt_numpos { ::my_one() * dis(gen) }; + float_type flt_numneg { ::my_one() * -dis(gen) }; + float_type flt_denom { ::my_zero() * dis(gen) }; + + const float_type div_result_zero_pos { flt_numpos / flt_denom }; + const float_type div_result_zero_neg { flt_numneg / flt_denom }; + + BOOST_TEST(result_is_ok = ((fpclassify(div_result_zero_pos) == FP_INFINITE) && result_is_ok)); + BOOST_TEST(result_is_ok = ((fpclassify(div_result_zero_neg) == FP_INFINITE) && signbit(div_result_zero_neg) && result_is_ok)); + } + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(8)); ++i) { static_cast(i); @@ -560,6 +596,33 @@ namespace local result_is_ok = (result_exp_small_is_ok && result_is_ok); } + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) + { + static_cast(i); + + const float_type arg_small_scale { float_type {::my_one() / 24 } * static_cast(dist(gen)) }; + + const float_type result_exp_small_scale { exp(arg_small_scale) }; + + using ctrl_type = boost::multiprecision::number, boost::multiprecision::et_off>; + + const ctrl_type result_ctrl { exp(ctrl_type { arg_small_scale }) }; + + bool result_exp_small_scale_is_ok + { + local::is_close_fraction + ( + result_exp_small_scale, + float_type { result_ctrl }, + std::numeric_limits::epsilon() * 512 + ) + }; + + BOOST_TEST(result_exp_small_scale_is_ok); + + result_is_ok = (result_exp_small_scale_is_ok && result_is_ok); + } + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) { static_cast(i); From 7742123051c607034395be29c48dcba652683e34 Mon Sep 17 00:00:00 2001 From: ckormanyos Date: Wed, 15 Jan 2025 17:39:52 +0100 Subject: [PATCH 16/16] Strengthen some testine edge cases --- test/test_various_edges.cpp | 146 +++++++++--------------------------- 1 file changed, 37 insertions(+), 109 deletions(-) diff --git a/test/test_various_edges.cpp b/test/test_various_edges.cpp index 922b3021e..60cf1d659 100644 --- a/test/test_various_edges.cpp +++ b/test/test_various_edges.cpp @@ -5,9 +5,15 @@ // 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 +#if defined(TEST_CPP_DOUBLE_FLOAT) #include +#endif #include #include @@ -499,15 +505,13 @@ namespace local { using float_type = FloatType; - std::mt19937_64 gen; - - gen.seed(time_point()); + std::mt19937_64 gen { time_point() }; std::uniform_real_distribution dist ( - static_cast(1.01L), - static_cast(1.08L) + static_cast(0.95L), + static_cast(1.05L) ); auto result_is_ok = true; @@ -623,134 +627,58 @@ namespace local result_is_ok = (result_exp_small_scale_is_ok && result_is_ok); } - for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) + for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(32)); ++i) { static_cast(i); + // Create a number with "fuzz", but which will essentially always round to integer-value 1. const float_type one { 1 }; const float_type arg_near_one { - one - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) + ( + one + * static_cast(dist(gen)) + * static_cast(dist(gen)) + * static_cast(dist(gen)) + ) + + float_type { 0.25F } * static_cast(dist(gen)) }; using ctrl_type = boost::multiprecision::number, boost::multiprecision::et_off>; - const float_type result_exp_n { exp(float_type { static_cast(arg_near_one) }) }; - const ctrl_type result_ctrl { exp(ctrl_type { static_cast(arg_near_one) }) }; + const float_type arg_near_one_to_use { static_cast(arg_near_one) }; + + const int arg_n { static_cast(arg_near_one_to_use) }; + + const float_type result_exp_n_pos { exp(float_type { arg_n }) }; + const ctrl_type result_ctrl_pos { exp(ctrl_type { arg_n }) }; - bool result_exp_n_is_ok + const float_type result_exp_n_neg { exp(float_type { -arg_n }) }; + const ctrl_type result_ctrl_neg { exp(ctrl_type { -arg_n }) }; + + bool result_exp_n_pos_is_ok { local::is_close_fraction ( - result_exp_n, - float_type { result_ctrl }, + result_exp_n_pos, + float_type { result_ctrl_pos }, std::numeric_limits::epsilon() * 512 ) }; - if(static_cast(arg_near_one) == 1) - { - const bool result_exp_n_at_one_is_ok - { - local::is_close_fraction - ( - result_exp_n, - ::my_exp1(), - std::numeric_limits::epsilon() * 16 - ) - }; - - result_exp_n_is_ok = (result_exp_n_at_one_is_ok && result_exp_n_is_ok); - } - - BOOST_TEST(result_exp_n_is_ok); - - result_is_ok = (result_exp_n_is_ok && result_is_ok); - } - - for(auto i = static_cast(UINT8_C(0)); i < static_cast(UINT8_C(16)); ++i) - { - static_cast(i); - - const float_type one { 1 }; - - const float_type - arg_near_one - { - one - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - * static_cast(dist(gen)) - }; - - using ctrl_type = boost::multiprecision::number, boost::multiprecision::et_off>; - - const float_type result_exp_n { exp(float_type { static_cast(-arg_near_one) }) }; - const ctrl_type result_ctrl { exp(ctrl_type { static_cast(-arg_near_one) }) }; - - bool result_exp_n_is_ok + bool result_exp_n_neg_is_ok { local::is_close_fraction ( - result_exp_n, - float_type { result_ctrl }, + result_exp_n_neg, + float_type { result_ctrl_neg }, std::numeric_limits::epsilon() * 512 ) }; - if(static_cast(-arg_near_one) == -1) - { - const bool result_exp_n_at_one_is_ok - { - local::is_close_fraction - ( - result_exp_n, - 1 / ::my_exp1(), - std::numeric_limits::epsilon() * 16 - ) - }; - - result_exp_n_is_ok = (result_exp_n_at_one_is_ok && result_exp_n_is_ok); - } + const bool result_exp_n_is_ok = (result_exp_n_pos_is_ok && result_exp_n_neg_is_ok); BOOST_TEST(result_exp_n_is_ok); @@ -795,9 +723,7 @@ namespace local { using float_type = FloatType; - std::mt19937_64 gen; - - gen.seed(time_point()); + std::mt19937_64 gen { time_point() }; std::uniform_real_distribution dist @@ -916,6 +842,7 @@ namespace local auto main() -> int { + #if defined(TEST_CPP_DOUBLE_FLOAT) { using float_type = boost::multiprecision::cpp_double_float; @@ -945,6 +872,7 @@ auto main() -> int local::test_exp_edge(); local::test_log_edge(); } + #endif { using float_backend_type = boost::multiprecision::cpp_bin_float<106, boost::multiprecision::digit_base_2, void, int, -1021, +1024>;