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