@@ -32,17 +32,20 @@ namespace boost::math::tools {
32
32
template <typename Real, typename Z = int64_t >
33
33
class simple_continued_fraction {
34
34
public:
35
- simple_continued_fraction (Real x) : x_{x} {
35
+ typedef Z int_type;
36
+
37
+ simple_continued_fraction (Real x) {
36
38
using std::floor ;
37
39
using std::abs ;
38
40
using std::sqrt ;
39
41
using std::isfinite;
42
+ const Real orig_x = x;
40
43
if (!isfinite (x)) {
41
- throw std::domain_error (" Cannot convert non-finites into continued fractions." );
44
+ throw std::domain_error (" Cannot convert non-finites into continued fractions." );
42
45
}
43
46
b_.reserve (50 );
44
47
Real bj = floor (x);
45
- b_.push_back (static_cast <Z >(bj));
48
+ b_.push_back (static_cast <int_type >(bj));
46
49
if (bj == x) {
47
50
b_.shrink_to_fit ();
48
51
return ;
@@ -54,14 +57,13 @@ class simple_continued_fraction {
54
57
}
55
58
Real C = f;
56
59
Real D = 0 ;
57
- int i = 0 ;
58
- // the "1 + i++" lets the error bound grow slowly with the number of convergents.
60
+ // the "1 + i" lets the error bound grow slowly with the number of convergents.
59
61
// I have not worked out the error propagation of the Modified Lentz's method to see if it does indeed grow at this rate.
60
62
// Numerical Recipes claims that no one has worked out the error analysis of the modified Lentz's method.
61
- while ( abs (f - x_) >= ( 1 + i++)* std::numeric_limits<Real>::epsilon ()*abs (x_))
62
- {
63
+ const Real eps_abs_orig_x = std::numeric_limits<Real>::epsilon ()*abs (orig_x);
64
+ for ( int i = 0 ; abs (f - orig_x) >= ( 1 + i)*eps_abs_orig_x; ++i) {
63
65
bj = floor (x);
64
- b_.push_back (static_cast <Z >(bj));
66
+ b_.push_back (static_cast <int_type >(bj));
65
67
x = 1 /(x-bj);
66
68
D += bj;
67
69
if (D == 0 ) {
@@ -78,29 +80,31 @@ class simple_continued_fraction {
78
80
// The shorter representation is considered the canonical representation,
79
81
// so if we compute a non-canonical representation, change it to canonical:
80
82
if (b_.size () > 2 && b_.back () == 1 ) {
81
- b_[b_. size () - 2 ] += 1 ;
82
- b_.resize (b_. size () - 1 ) ;
83
+ b_. pop_back () ;
84
+ b_.back () += 1 ;
83
85
}
84
86
b_.shrink_to_fit ();
85
-
86
- for (size_t i = 1 ; i < b_.size (); ++i) {
87
+
88
+ const size_t size_ = b_.size ();
89
+ for (size_t i = 1 ; i < size_; ++i) {
87
90
if (b_[i] <= 0 ) {
88
91
std::ostringstream oss;
89
92
oss << " Found a negative partial denominator: b[" << i << " ] = " << b_[i] << " ."
90
93
#ifndef BOOST_MATH_STANDALONE
91
- << " This means the integer type '" << boost::core::demangle (typeid (Z ).name ())
94
+ << " This means the integer type '" << boost::core::demangle (typeid (int_type ).name ())
92
95
#else
93
- << " This means the integer type '" << typeid (Z ).name ()
96
+ << " This means the integer type '" << typeid (int_type ).name ()
94
97
#endif
95
98
<< " ' has overflowed and you need to use a wider type,"
96
99
<< " or there is a bug." ;
97
100
throw std::overflow_error (oss.str ());
98
101
}
99
102
}
100
103
}
101
-
104
+
102
105
Real khinchin_geometric_mean () const {
103
- if (b_.size () == 1 ) {
106
+ const size_t size_ = b_.size ();
107
+ if (size_ == 1 ) {
104
108
return std::numeric_limits<Real>::quiet_NaN ();
105
109
}
106
110
using std::log ;
@@ -110,53 +114,57 @@ class simple_continued_fraction {
110
114
// A random partial denominator has ~80% chance of being in this table:
111
115
const std::array<Real, 7 > logs{std::numeric_limits<Real>::quiet_NaN (), Real (0 ), log (static_cast <Real>(2 )), log (static_cast <Real>(3 )), log (static_cast <Real>(4 )), log (static_cast <Real>(5 )), log (static_cast <Real>(6 ))};
112
116
Real log_prod = 0 ;
113
- for (size_t i = 1 ; i < b_. size () ; ++i) {
114
- if (b_[i] < static_cast <Z >(logs.size ())) {
117
+ for (size_t i = 1 ; i < size_ ; ++i) {
118
+ if (b_[i] < static_cast <int_type >(logs.size ())) {
115
119
log_prod += logs[b_[i]];
116
120
}
117
121
else
118
122
{
119
123
log_prod += log (static_cast <Real>(b_[i]));
120
124
}
121
125
}
122
- log_prod /= (b_. size () -1 );
126
+ log_prod /= (size_ -1 );
123
127
return exp (log_prod);
124
128
}
125
-
129
+
126
130
Real khinchin_harmonic_mean () const {
127
- if (b_.size () == 1 ) {
131
+ const size_t size_ = b_.size ();
132
+ if (size_ == 1 ) {
128
133
return std::numeric_limits<Real>::quiet_NaN ();
129
134
}
130
- Real n = b_. size () - 1 ;
135
+ Real n = size_ - 1 ;
131
136
Real denom = 0 ;
132
- for (size_t i = 1 ; i < b_. size () ; ++i) {
137
+ for (size_t i = 1 ; i < size_ ; ++i) {
133
138
denom += 1 /static_cast <Real>(b_[i]);
134
139
}
135
140
return n/denom;
136
141
}
137
-
138
- const std::vector<Z>& partial_denominators () const {
142
+
143
+ // Note that this also includes the integer part (i.e. all the coefficients)
144
+ const std::vector<int_type>& partial_denominators () const {
139
145
return b_;
140
146
}
141
-
147
+
142
148
template <typename T, typename Z2>
143
149
friend std::ostream& operator <<(std::ostream& out, simple_continued_fraction<T, Z2>& scf);
150
+ protected:
151
+ simple_continued_fraction () = default ;
144
152
153
+ // Note that non-const operations may result in invalid simple continued fraction
154
+ std::vector<int_type>& partial_denominators () {
155
+ return b_;
156
+ }
145
157
private:
146
- const Real x_;
147
- std::vector<Z> b_;
158
+ std::vector<int_type> b_{};
148
159
};
149
160
150
161
151
162
template <typename Real, typename Z2>
152
163
std::ostream& operator <<(std::ostream& out, simple_continued_fraction<Real, Z2>& scf) {
153
164
constexpr const int p = std::numeric_limits<Real>::max_digits10;
154
- if constexpr (p == 2147483647 ) {
155
- out << std::setprecision (scf.x_ .backend ().precision ());
156
- } else {
157
- out << std::setprecision (p);
158
- }
159
-
165
+ static_assert (p != 2147483647 , " numeric_limits<Real>::max_digits10 == 2147483647" );
166
+ out << std::setprecision (p);
167
+
160
168
out << " [" << scf.b_ .front ();
161
169
if (scf.b_ .size () > 1 )
162
170
{
0 commit comments