/////////////////////////////////////////////////////////////// // Copyright Christopher Kormanyos 2024 - 2025. // Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt // This version of "performance_test_df_local.cpp" is intended // for specific function-by-function comparisons with cpp_double_double. #include #include #include #include #if defined(BOOST_HAS_FLOAT128) #include #endif #include #include #include #include #include #include #include #include #include #if defined(_MSC_VER) && !defined(__GNUC__) #define STOPWATCH_NODISCARD #else #if (defined(__cplusplus) && (__cplusplus >= 201703L)) #define STOPWATCH_NODISCARD [[nodiscard]] // NOLINT(cppcoreguidelines-macro-usage) #else #define STOPWATCH_NODISCARD #endif #endif namespace concurrency { // See also: https://godbolt.org/z/37a4n9f4Y struct stopwatch { public: using time_point_type = std::uintmax_t; auto reset() -> void { m_start = now(); } template static auto elapsed_time(const stopwatch& my_stopwatch) noexcept -> RepresentationRequestedTimeType { using local_time_type = RepresentationRequestedTimeType; return local_time_type { static_cast(my_stopwatch.elapsed()) / local_time_type { UINTMAX_C(1000000000) } }; } STOPWATCH_NODISCARD static auto now() -> time_point_type { #if defined(__CYGWIN__) return static_cast(std::clock()); #else timespec ts { }; const int ntsp { timespec_get(&ts, TIME_UTC) }; static_cast(ntsp); return static_cast ( static_cast(static_cast(ts.tv_sec) * UINTMAX_C(1000000000)) + static_cast(ts.tv_nsec) ); #endif } private: time_point_type m_start { now() }; // NOLINT(readability-identifier-naming) STOPWATCH_NODISCARD auto elapsed() const -> time_point_type { const time_point_type stop { now() }; #if defined(__CYGWIN__) const time_point_type elapsed_ns { static_cast ( static_cast(static_cast(stop - m_start) * UINTMAX_C(1000000000)) / static_cast(CLOCKS_PER_SEC) ) }; #else const time_point_type elapsed_ns { static_cast ( stop - m_start ) }; #endif return elapsed_ns; } }; } // namespace concurrency namespace util { template(UINT8_C(10)), const bool UpperCase = true> auto baselexical_cast(const UnsignedIntegerType& u, char* first, char* last) -> const char* { constexpr auto my_base = static_cast(BaseRepresentation); const auto result = std::to_chars(first, last, u, my_base); return result.ptr; } template auto util_pseudorandom_time_point_seed() -> UnsignedIntegralType { using stopwatch_type = concurrency::stopwatch; return static_cast(stopwatch_type::now()); } } // namespace util std::uniform_int_distribution dist_sgn(UINT32_C( 0), UINT32_C( 1)); // NOLINT(cert-err58-cpp,cppcoreguidelines-avoid-non-const-global-variables) std::uniform_int_distribution dist_dig(UINT32_C(0x31), UINT32_C( 0x39)); // NOLINT(cert-err58-cpp,cppcoreguidelines-avoid-non-const-global-variables) using eng_sgn_type = std::ranlux24; using eng_dig_type = std::minstd_rand0; using eng_exp_type = std::mt19937; eng_sgn_type eng_sgn; // NOLINT(cert-msc32-c,cert-msc51-cpp,cert-err58-cpp,cppcoreguidelines-avoid-non-const-global-variables) eng_dig_type eng_dig; // NOLINT(cert-msc32-c,cert-msc51-cpp,cert-err58-cpp,cppcoreguidelines-avoid-non-const-global-variables) template auto generate_wide_decimal_value(bool is_positive = false, int digits10_to_get = std::numeric_limits::digits10 - 2) -> FloatingPointTypeWithStringConstruction { using local_floating_point_type = FloatingPointTypeWithStringConstruction; static_assert(std::numeric_limits::digits10 > static_cast(INT8_C(9)), "Error: Floating-point type destination does not have enough digits10"); std::string str_x(static_cast(digits10_to_get), '0'); std::generate(str_x.begin(), str_x.end(), []() // NOLINT(modernize-use-trailing-return-type,-warnings-as-errors) { return static_cast(dist_dig(eng_dig)); }); // Insert a decimal point. str_x.insert(static_cast(UINT8_C(1)), static_cast(UINT8_C(1)), '.'); // Insert either a positive sign or a negative sign // (always one or the other) depending on the sign of x. const auto sign_char_to_insert = static_cast ( is_positive ? '+' : static_cast((dist_sgn(eng_sgn) != static_cast(UINT8_C(0))) ? '+' : '-') ); str_x.insert(static_cast(UINT8_C(0)), static_cast(UINT8_C(1)), sign_char_to_insert); return local_floating_point_type(str_x.c_str()); } template auto is_close_fraction(const NumericType& a, const NumericType& b, const NumericType& tol) noexcept -> bool { using std::fabs; auto result_is_ok = bool { }; if(b == static_cast(0)) { result_is_ok = (fabs(a - b) < tol); // LCOV_EXCL_LINE } else { const auto delta = fabs(1 - (a / b)); result_is_ok = (delta < tol); } return result_is_ok; } auto main() -> int { using dbl_float_type = boost::multiprecision::cpp_double_double; using dec_float_type = boost::multiprecision::number, boost::multiprecision::et_off>; #if defined(BOOST_HAS_FLOAT128) using flt_float_type = boost::multiprecision::float128; #endif constexpr std::size_t trials { UINT32_C(100000) }; std::vector dbl_float_a_vec (trials); std::vector dbl_float_b_vec (dbl_float_a_vec.size()); std::vector dec_float_a_vec (dbl_float_a_vec.size()); std::vector dec_float_b_vec (dbl_float_a_vec.size()); #if defined(BOOST_HAS_FLOAT128) std::vector flt_float_a_vec (dbl_float_a_vec.size()); std::vector flt_float_b_vec (dbl_float_a_vec.size()); #endif for(std::size_t index { UINT8_C(0) }; index < dbl_float_a_vec.size(); ++index) { if(std::size_t { index % unsigned { UINT32_C(0x800) } } == std::size_t { UINT8_C(0) }) { eng_sgn.seed(util::util_pseudorandom_time_point_seed()); eng_dig.seed(util::util_pseudorandom_time_point_seed()); } auto gen { [](bool is_positive, const dbl_float_type& gate = 0 ) { dbl_float_type val { }; if(is_positive && gate != 0) { while((val = generate_wide_decimal_value(true)) < gate) { ; } } else { val = generate_wide_decimal_value(true); } return val; } }; dbl_float_a_vec[index] = dbl_float_type { gen(true, dbl_float_type { 9 } / 8) }; dbl_float_b_vec[index] = dbl_float_type { gen(true, dbl_float_type { 9 } / 8) }; dec_float_a_vec[index] = dec_float_type { dbl_float_a_vec[index] }; dec_float_b_vec[index] = dec_float_type { dbl_float_b_vec[index] }; #if defined(BOOST_HAS_FLOAT128) flt_float_a_vec[index] = flt_float_type { dbl_float_a_vec[index] }; flt_float_b_vec[index] = flt_float_type { dbl_float_b_vec[index] }; #endif } std::vector dbl_float_c_vec (dbl_float_a_vec.size()); std::vector dec_float_c_vec (dbl_float_a_vec.size()); #if defined(BOOST_HAS_FLOAT128) std::vector flt_float_c_vec (dbl_float_a_vec.size()); #endif using stopwatch_type = concurrency::stopwatch; stopwatch_type my_stopwatch { }; for(std::size_t count { UINT8_C(0) }; count < trials; ++count) { dbl_float_c_vec[count] = boost::multiprecision::cyl_bessel_j(dbl_float_a_vec[count], dbl_float_b_vec[count]); } const double elapsed_dbl { stopwatch_type::elapsed_time(my_stopwatch) }; my_stopwatch.reset(); for(std::size_t count { UINT8_C(0) }; count < trials; ++count) { dec_float_c_vec[count] = boost::multiprecision::cyl_bessel_j(dec_float_a_vec[count], dec_float_b_vec[count]); } const double elapsed_dec { stopwatch_type::elapsed_time(my_stopwatch) }; #if defined(BOOST_HAS_FLOAT128) my_stopwatch.reset(); for(std::size_t count { UINT8_C(0) }; count < trials; ++count) { flt_float_c_vec[count] = boost::multiprecision::cyl_bessel_j(flt_float_a_vec[count], flt_float_b_vec[count]); } const double elapsed_flt { stopwatch_type::elapsed_time(my_stopwatch) }; #endif std::stringstream strm { }; strm << std::fixed << std::setprecision(3) << "elapsed_dbl : " << elapsed_dbl << "s\n" << std::fixed << std::setprecision(3) << "elapsed_dec : " << elapsed_dec << "s\n" #if defined(BOOST_HAS_FLOAT128) << std::fixed << std::setprecision(3) << "elapsed_flt : " << elapsed_flt << "s\n" #endif << std::fixed << std::setprecision(3) << "ratio (dec/dbl) : " << elapsed_dec / elapsed_dbl << "\n" #if defined(BOOST_HAS_FLOAT128) << std::fixed << std::setprecision(3) << "ratio (flt/dbl) : " << elapsed_flt / elapsed_dbl << "\n" #endif ; std::cout << strm.str() << std::endl; std::cout << "verifying results...\n"; std::size_t count { UINT8_C(0) }; constexpr dbl_float_type tol_dbl { std::numeric_limits::epsilon() * 1024 }; for(const auto& lhs : dbl_float_c_vec) { const dbl_float_type ctrl_dec { dec_float_c_vec[count] }; BOOST_TEST(is_close_fraction(lhs, ctrl_dec, tol_dbl)); #if defined(BOOST_HAS_FLOAT128) const dbl_float_type ctrl_flt { flt_float_c_vec[count] }; BOOST_TEST(is_close_fraction(lhs, ctrl_flt, tol_dbl)); #endif ++count; } return boost::report_errors(); }