Skip to content

Commit fc508e8

Browse files
authored
Merge pull request #711 from boostorg/more_coverage
Obtain more code coverage
2 parents 55bf069 + 5b53322 commit fc508e8

33 files changed

+2373
-1019
lines changed

doc/tutorial_cpp_double_fp_backend.qbk

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -24,26 +24,27 @@
2424
} } // namespaces
2525

2626
The `cpp_double_fp_backend` back-end is the sum of two IEEE floating-point numbers
27-
combined to create a type having rougly twice the composite width of one of its parts.
27+
combined to create a type having a composite width rougly twice that as one of its parts.
2828
The `cpp_double_fp_backend` back-end is used in conjunction with `number`
2929
and acts as an entirely C++ header only floating-point number type.
3030

3131
The implementation relies on double-word arithmetic which is a technique
3232
used to represent a real number as the sum of two floating-point numbers.
33-
Other commonly used names for this include `double-double` of `double-word` arithmetic.
33+
Other commonly used names for this include `double-double` or `double-word` arithmetic.
3434

3535
The `cpp_double_fp_backend` types have fixed width and do not allocate.
36-
The type `cpp_double_double`, for instance, (composed of two double-precision IEEE floating-point numebers)
37-
has (on most common systems) 106 binary digits of precision and approximately
36+
The type `cpp_double_double`, for instance, is composed of two built-in `double` components.
37+
On most common systems, built-in `double` is a double-precision IEEE floating-point number.
38+
This results in a `cpp_double_double` that has 106 binary digits and approximately
3839
32 decimal digits of precision.
3940

40-
The exponent ranges of the types are slightly limited (on the negative side) compared to those of the composite type.
41-
Consider again the type `cpp_double_double`, composed of two double-precision IEEE double-precision
41+
The exponent ranges of the types are slightly limited (on the negative side) compared to those of the composing type.
42+
Consider again the type `cpp_double_double`, which is built from two double-precision IEEE double-precision
4243
floating-point numebers. On common systems, this type has a maximum decimal exponent of 308
43-
(as does a single double-precision floating point number). The negative minimum exponent, however,
44-
is about -291, which is less range than the -307 from standalone double. The reason for
45-
the limitation is because the composite lower-limb has lower value than its upper limb and would
46-
underflow or become subnormal if the upper limb had its usual minimum value.
44+
(the same as one single double-precision floating point number). The negative minimum exponent, however,
45+
is about -291, which is less range than -307 from standalone double. The reason for
46+
the limitation is because the composite lower-limb has lower value than its upper limb.
47+
The composite type would easily underflow or become subnormal if the upper limb had its usual minimum value.
4748

4849
There is full standard library and `std::numeric_limits` support available for this type.
4950

@@ -55,8 +56,8 @@ also in strict ANSI mode.
5556

5657
Run-time performance is a top-level requirement for the `cpp_double_fp_backend` types.
5758
The types still do, however, support infinities, NaNs and (of course) zeros.
58-
Signed negative zero, however, is not supported (in favor of efficiency)
59-
and all zeros are treated as positive.
59+
Signed negative zero, however, is not supported (in favor of efficiency).
60+
All zeros are treated as positive.
6061

6162
The `cpp_double_fp_backend` types interoperate with Boost.Math and Boost.Math.Constants.
6263
This offers the wealth of Boost-related mathematical tools instantiated with
@@ -66,19 +67,19 @@ Things you should know when using the `cpp_double_fp_backend` types:
6667

6768
* Although the types are created from two individual IEEE floating-point components, they specifically and clearly are not IEEE types in their composite forms.
6869
* As a result these types can behave subtly differently from IEEE floating-point types.
69-
* The types can not be used with compiler variations of _fast_-_math_ (i.e., `-ffast-math` on GCC can not be used and `/fp:precise` is mandatory on MSVC compilers). This is because the algorithms (particularly those for addition, subtraction, multiplication and square root) rely on precise floating-point rouunding.
70+
* The types can not be used with certain compiler variations of _fast_-_math_. On GCC/clang, for instance, `-ffast-math` can not be used (use either the default or explicitly set `-fno-fast-math`). On MSVC `/fp:fast` can not be used and `/fp:precise` (the default) is mandatory on MSVC compilers. This is because the algorithms, in particular those for addition, subtraction, multiplication, division and square root, rely on precise floating-point rounding.
7071
* The composite types are not as precise as their constituents. For information on error-bounds, see Joldes et al. in the references below.
7172
* There are `std::numeric_limits` specializations for these types.
7273
* Large parts (but not all) of the `cpp_double_fp_backend` implementation are `constexpr`. Future evolution is anticipated to make this library entirely `constexpr`.
7374
* Conversions to and from string internally use an intermediate `cpp_bin_float` value (which is a bit awkward may be eliminated in future refinements).
7475

75-
The `cpp_double_fp_backend` back-end has been inspired by original works and types such as the historical `doubledouble`
76-
and more. These include the following:
76+
The `cpp_double_fp_backend` back-end has been inspired by original works and types.
77+
These include the historical `doubledouble` and more, as listed below.
7778

7879
* K. Briggs, the `doubledouble` library, 1998.
7980
* V. Shoup, the class `quad_float` in the NTL number-theory library [@https://libntl.org].
8081
* Yozo Hida, X. Li, and D. H. Bailey, Quad-Double Arithmetic: Algorithms, Implementation, and Application, Lawrence Berkeley National Laboratory Technical Report LBNL-46996 (2000). Also Y. Hida et al., Library for double-double and quad-double arithmetic [@https://web.mit.edu/tabbott/Public/quaddouble-debian/qd-2.3.4-old/docs/qd.pdf].
8182
* Mioara Maria Joldes, Jean-Michel Muller, Valentina Popescu. Tight and rigourous error bounds for basic building blocks of double-word arithmetic. ACM Transactions on Mathematical Software, 2017, 44 (2), pp. 1 - 27. ff10.1145/3121432ff. ffhal-01351529v3f.
82-
* The foundational `cpp_double_fp_backend` draft was originally created by Fahad Syed in BoostGSoC2021 _multiprecision_ [@https://github.com/BoostGSoC21/multiprecision].
83+
* The foundational `cpp_double_fp_backend` draft was originally created by Fahad Syed in Boost GSoC2021 multiprecision project. Its source code can be found at [@https://github.com/BoostGSoC21/multiprecision].
8384

8485
[endsect]

example/exercise_threading_log_agm.cpp

Lines changed: 85 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
///////////////////////////////////////////////////////////////////////////////
2-
// Copyright Christopher Kormanyos 2020 - 2021.
2+
// Copyright Christopher Kormanyos 2020 - 2025.
33
// Distributed under the Boost Software License, Version 1.0.
44
// (See accompanying file LICENSE_1_0.txt or copy at
55
// http://www.boost.org/LICENSE_1_0.txt)
@@ -25,73 +25,80 @@
2525
// We find the following performance data here:
2626
// https://github.com/boostorg/multiprecision/pull/213
2727
//
28+
// New numbers 2025-07-20 at 301 decimal digits
29+
// --------------------------------------------
30+
//
2831
// cpp_dec_float:
29-
// result_is_ok_concurrent: true, calculation_time_concurrent: 18.1s
30-
// result_is_ok_sequential: true, calculation_time_sequential: 48.5s
32+
// result_is_ok_concurrent: true, calculation_time_concurrent: 2.1s
33+
// result_is_ok_sequential: true, calculation_time_sequential: 14.7s
3134
//
3235
// cpp_bin_float:
33-
// result_is_ok_concurrent: true, calculation_time_concurrent: 18.7s
34-
// result_is_ok_sequential: true, calculation_time_sequential: 50.4s
36+
// result_is_ok_concurrent: true, calculation_time_concurrent: 0.28s
37+
// result_is_ok_sequential: true, calculation_time_sequential: 1.88s
3538
//
3639
// gmp_float:
37-
// result_is_ok_concurrent: true, calculation_time_concurrent: 3.3s
38-
// result_is_ok_sequential: true, calculation_time_sequential: 12.4s
40+
// result_is_ok_concurrent: true, calculation_time_concurrent: 0.11s
41+
// result_is_ok_sequential: true, calculation_time_sequential: 0.73s
3942
//
4043
// mpfr_float:
41-
// result_is_ok_concurrent: true, calculation_time_concurrent: 0.6s
42-
// result_is_ok_sequential: true, calculation_time_sequential: 1.9s
44+
// result_is_ok_concurrent: true, calculation_time_concurrent: 0.05s
45+
// result_is_ok_sequential: true, calculation_time_sequential: 0.24s
4346

4447
#include <array>
4548
#include <atomic>
49+
#include <chrono>
4650
#include <cstddef>
4751
#include <cstdint>
4852
#include <iomanip>
4953
#include <iostream>
5054
#include <limits>
55+
#include <sstream>
5156
#include <thread>
5257
#include <vector>
5358

5459
#include <boost/math/constants/constants.hpp>
5560
#include <boost/math/special_functions/prime.hpp>
5661

57-
#define BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_CPP_DEC_FLOAT 101
58-
#define BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_GMP_FLOAT 102
59-
#define BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_CPP_BIN_FLOAT 103
60-
#define BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_MPFR_FLOAT 104
62+
#define BOOST_MP_EXERCISE_THREADING_BACKEND_CPP_DEC_FLOAT 101
63+
#define BOOST_MP_EXERCISE_THREADING_BACKEND_GMP_FLOAT 102
64+
#define BOOST_MP_EXERCISE_THREADING_BACKEND_CPP_BIN_FLOAT 103
65+
#define BOOST_MP_EXERCISE_THREADING_BACKEND_MPFR_FLOAT 104
6166

62-
#if !defined(BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_TYPE)
63-
#define BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_TYPE BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_CPP_DEC_FLOAT
64-
//#define BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_TYPE BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_CPP_BIN_FLOAT
65-
//#define BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_TYPE BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_GMP_FLOAT
66-
//#define BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_TYPE BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_MPFR_FLOAT
67+
#if !defined(BOOST_MP_EXERCISE_THREADING_BACKEND_TYPE)
68+
#define BOOST_MP_EXERCISE_THREADING_BACKEND_TYPE BOOST_MP_EXERCISE_THREADING_BACKEND_CPP_DEC_FLOAT
69+
//#define BOOST_MP_EXERCISE_THREADING_BACKEND_TYPE BOOST_MP_EXERCISE_THREADING_BACKEND_CPP_BIN_FLOAT
70+
//#define BOOST_MP_EXERCISE_THREADING_BACKEND_TYPE BOOST_MP_EXERCISE_THREADING_BACKEND_GMP_FLOAT
71+
//#define BOOST_MP_EXERCISE_THREADING_BACKEND_TYPE BOOST_MP_EXERCISE_THREADING_BACKEND_MPFR_FLOAT
6772
#endif
6873

69-
#if (BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_TYPE == BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_CPP_DEC_FLOAT)
74+
constexpr unsigned local_mp_digits { 301U };
75+
76+
#if (BOOST_MP_EXERCISE_THREADING_BACKEND_TYPE == BOOST_MP_EXERCISE_THREADING_BACKEND_CPP_DEC_FLOAT)
7077
#include <boost/multiprecision/cpp_dec_float.hpp>
7178

72-
using big_float_type = boost::multiprecision::number<boost::multiprecision::cpp_dec_float<501>,
79+
using big_float_type = boost::multiprecision::number<boost::multiprecision::cpp_dec_float<local_mp_digits>,
7380
boost::multiprecision::et_off>;
7481

75-
#elif (BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_TYPE == BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_CPP_BIN_FLOAT)
82+
#elif (BOOST_MP_EXERCISE_THREADING_BACKEND_TYPE == BOOST_MP_EXERCISE_THREADING_BACKEND_CPP_BIN_FLOAT)
7683
#include <boost/multiprecision/cpp_bin_float.hpp>
7784

78-
using big_float_type = boost::multiprecision::number<boost::multiprecision::cpp_bin_float<501>,
85+
using big_float_type = boost::multiprecision::number<boost::multiprecision::cpp_bin_float<local_mp_digits>,
7986
boost::multiprecision::et_off>;
8087

81-
#elif (BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_TYPE == BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_GMP_FLOAT)
88+
#elif (BOOST_MP_EXERCISE_THREADING_BACKEND_TYPE == BOOST_MP_EXERCISE_THREADING_BACKEND_GMP_FLOAT)
8289
#include <boost/multiprecision/gmp.hpp>
8390

84-
using big_float_type = boost::multiprecision::number<boost::multiprecision::gmp_float<501>,
91+
using big_float_type = boost::multiprecision::number<boost::multiprecision::gmp_float<local_mp_digits>,
8592
boost::multiprecision::et_off>;
8693

87-
#elif (BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_TYPE == BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_MPFR_FLOAT)
94+
#elif (BOOST_MP_EXERCISE_THREADING_BACKEND_TYPE == BOOST_MP_EXERCISE_THREADING_BACKEND_MPFR_FLOAT)
8895
#include <boost/multiprecision/mpfr.hpp>
8996

90-
using big_float_type = boost::multiprecision::number<boost::multiprecision::mpfr_float_backend<501>,
97+
using big_float_type = boost::multiprecision::number<boost::multiprecision::mpfr_float_backend<local_mp_digits>,
9198
boost::multiprecision::et_off>;
9299

93100
#else
94-
#error BOOST_MULTIPRECISION_EXERCISE_THREADING_BACKEND_TYPE is undefined.
101+
#error BOOST_MP_EXERCISE_THREADING_BACKEND_TYPE is undefined.
95102
#endif
96103

97104
namespace boost { namespace multiprecision { namespace exercise_threading {
@@ -112,8 +119,8 @@ void parallel_for(index_type start,
112119
static const unsigned int number_of_threads_total =
113120
((number_of_threads_hint == 0U) ? 4U : number_of_threads_hint);
114121

115-
// Use only 3/4 of the available cores.
116-
static const unsigned int number_of_threads = number_of_threads_total - (number_of_threads_total / 8U);
122+
// Use 7/8 of the available cores (leaving a core or two free on modern systems).
123+
static const unsigned int number_of_threads = number_of_threads_total - ((number_of_threads_total + 4U) / 8U);
117124

118125
std::cout << "Executing with " << number_of_threads << " threads" << std::endl;
119126

@@ -310,7 +317,7 @@ bool log_agm_concurrent(float& calculation_time)
310317

311318
std::size_t concurrent_log_agm_count = 0U;
312319

313-
const std::clock_t start = std::clock();
320+
const auto start = std::chrono::high_resolution_clock::now();
314321

315322
boost::multiprecision::exercise_threading::detail::my_concurrency::parallel_for
316323
(
@@ -342,7 +349,7 @@ bool log_agm_concurrent(float& calculation_time)
342349
<< log_results.size()
343350
<< ". Total processed so far: "
344351
<< std::fixed
345-
<< std::setprecision(1)
352+
<< std::setprecision(2)
346353
<< (100.0F * float(concurrent_log_agm_count)) / float(log_results.size())
347354
<< "%."
348355
<< "\r";
@@ -352,7 +359,9 @@ bool log_agm_concurrent(float& calculation_time)
352359
}
353360
);
354361

355-
calculation_time = static_cast<float>(std::clock() - start) / static_cast<float>(CLOCKS_PER_SEC);
362+
const auto stop = std::chrono::high_resolution_clock::now();
363+
364+
calculation_time = static_cast<float>(std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count()) / 1000.0F;
356365

357366
std::cout << std::endl;
358367

@@ -371,7 +380,10 @@ bool log_agm_concurrent(float& calculation_time)
371380
result_is_ok &= (close_fraction < tol);
372381
}
373382

374-
std::cout << std::boolalpha << result_is_ok << std::endl;
383+
std::stringstream strm { };
384+
strm << std::boolalpha << result_is_ok;
385+
386+
std::cout << strm.str() << std::endl;
375387

376388
return result_is_ok;
377389
}
@@ -384,7 +396,7 @@ bool log_agm_sequential(float& calculation_time)
384396
std::vector<FloatingPointType> log_results(count);
385397
std::vector<FloatingPointType> log_control(count);
386398

387-
const std::clock_t start = std::clock();
399+
const auto start = std::chrono::high_resolution_clock::now();
388400

389401
for(std::size_t i = 0U; i < log_results.size(); ++i)
390402
{
@@ -404,14 +416,16 @@ bool log_agm_sequential(float& calculation_time)
404416
<< log_results.size()
405417
<< ". Total processed so far: "
406418
<< std::fixed
407-
<< std::setprecision(1)
419+
<< std::setprecision(2)
408420
<< (100.0F * float(sequential_log_agm_count)) / float(log_results.size())
409421
<< "%."
410422
<< "\r";
411423
}
412424
}
413425

414-
calculation_time = static_cast<float>(std::clock() - start) / static_cast<float>(CLOCKS_PER_SEC);
426+
const auto stop = std::chrono::high_resolution_clock::now();
427+
428+
calculation_time = static_cast<float>(std::chrono::duration_cast<std::chrono::milliseconds>(stop - start).count()) / 1000.0F;
415429

416430
std::cout << std::endl;
417431

@@ -430,7 +444,10 @@ bool log_agm_sequential(float& calculation_time)
430444
result_is_ok &= (close_fraction < tol);
431445
}
432446

433-
std::cout << std::boolalpha << result_is_ok << std::endl;
447+
std::stringstream strm { };
448+
strm << std::boolalpha << result_is_ok;
449+
450+
std::cout << strm.str() << std::endl;
434451

435452
return result_is_ok;
436453
}
@@ -442,31 +459,41 @@ int main()
442459
<< " primes"
443460
<< std::endl;
444461

445-
float calculation_time_concurrent;
446-
const bool result_is_ok_concurrent = log_agm_concurrent<big_float_type>(calculation_time_concurrent);
462+
float calculation_time_concurrent { };
463+
float calculation_time_sequential { };
447464

448-
float calculation_time_sequential;
465+
const bool result_is_ok_concurrent = log_agm_concurrent<big_float_type>(calculation_time_concurrent);
449466
const bool result_is_ok_sequential = log_agm_sequential<big_float_type>(calculation_time_sequential);
450467

451468
std::cout << std::endl;
452469

453-
std::cout << "result_is_ok_concurrent: "
454-
<< std::boolalpha
455-
<< result_is_ok_concurrent
456-
<< ", calculation_time_concurrent: "
457-
<< std::fixed
458-
<< std::setprecision(1)
459-
<< calculation_time_concurrent
460-
<< "s"
461-
<< std::endl;
470+
{
471+
std::stringstream strm { };
472+
473+
strm << "result_is_ok_concurrent: "
474+
<< std::boolalpha
475+
<< result_is_ok_concurrent
476+
<< ", calculation_time_concurrent: "
477+
<< std::fixed
478+
<< std::setprecision(2)
479+
<< calculation_time_concurrent
480+
<< "s";
481+
482+
std::cout << strm.str() << std::endl;
483+
}
462484

463-
std::cout << "result_is_ok_sequential: "
464-
<< std::boolalpha
465-
<< result_is_ok_sequential
466-
<< ", calculation_time_sequential: "
467-
<< std::fixed
468-
<< std::setprecision(1)
469-
<< calculation_time_sequential
470-
<< "s"
471-
<< std::endl;
485+
{
486+
std::stringstream strm { };
487+
488+
strm << "result_is_ok_sequential: "
489+
<< std::boolalpha
490+
<< result_is_ok_sequential
491+
<< ", calculation_time_sequential: "
492+
<< std::fixed
493+
<< std::setprecision(2)
494+
<< calculation_time_sequential
495+
<< "s";
496+
497+
std::cout << strm.str() << std::endl;
498+
}
472499
}

0 commit comments

Comments
 (0)