@@ -32,17 +32,28 @@ 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." );
45
+ }
46
+
47
+ constexpr int p = std::numeric_limits<Real>::max_digits10;
48
+ if constexpr (p == 2147483647 ) {
49
+ precision_ = orig_x.backend ().precision ();
50
+ } else {
51
+ precision_ = p;
42
52
}
53
+
43
54
b_.reserve (50 );
44
55
Real bj = floor (x);
45
- b_.push_back (static_cast <Z >(bj));
56
+ b_.push_back (static_cast <int_type >(bj));
46
57
if (bj == x) {
47
58
b_.shrink_to_fit ();
48
59
return ;
@@ -54,14 +65,13 @@ class simple_continued_fraction {
54
65
}
55
66
Real C = f;
56
67
Real D = 0 ;
57
- int i = 0 ;
58
- // the "1 + i++" lets the error bound grow slowly with the number of convergents.
68
+ // the "1 + i" lets the error bound grow slowly with the number of convergents.
59
69
// 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
70
// 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
- {
71
+ const Real eps_abs_orig_x = std::numeric_limits<Real>::epsilon ()*abs (orig_x);
72
+ for ( int i = 0 ; abs (f - orig_x) >= ( 1 + i)*eps_abs_orig_x; ++i) {
63
73
bj = floor (x);
64
- b_.push_back (static_cast <Z >(bj));
74
+ b_.push_back (static_cast <int_type >(bj));
65
75
x = 1 /(x-bj);
66
76
D += bj;
67
77
if (D == 0 ) {
@@ -78,29 +88,31 @@ class simple_continued_fraction {
78
88
// The shorter representation is considered the canonical representation,
79
89
// so if we compute a non-canonical representation, change it to canonical:
80
90
if (b_.size () > 2 && b_.back () == 1 ) {
81
- b_[b_. size () - 2 ] += 1 ;
82
- b_.resize (b_. size () - 1 ) ;
91
+ b_. pop_back () ;
92
+ b_.back () += 1 ;
83
93
}
84
94
b_.shrink_to_fit ();
85
-
86
- for (size_t i = 1 ; i < b_.size (); ++i) {
95
+
96
+ const size_t size_ = b_.size ();
97
+ for (size_t i = 1 ; i < size_; ++i) {
87
98
if (b_[i] <= 0 ) {
88
99
std::ostringstream oss;
89
100
oss << " Found a negative partial denominator: b[" << i << " ] = " << b_[i] << " ."
90
101
#ifndef BOOST_MATH_STANDALONE
91
- << " This means the integer type '" << boost::core::demangle (typeid (Z ).name ())
102
+ << " This means the integer type '" << boost::core::demangle (typeid (int_type ).name ())
92
103
#else
93
- << " This means the integer type '" << typeid (Z ).name ()
104
+ << " This means the integer type '" << typeid (int_type ).name ()
94
105
#endif
95
106
<< " ' has overflowed and you need to use a wider type,"
96
107
<< " or there is a bug." ;
97
108
throw std::overflow_error (oss.str ());
98
109
}
99
110
}
100
111
}
101
-
112
+
102
113
Real khinchin_geometric_mean () const {
103
- if (b_.size () == 1 ) {
114
+ const size_t size_ = b_.size ();
115
+ if (size_ == 1 ) {
104
116
return std::numeric_limits<Real>::quiet_NaN ();
105
117
}
106
118
using std::log ;
@@ -110,53 +122,56 @@ class simple_continued_fraction {
110
122
// A random partial denominator has ~80% chance of being in this table:
111
123
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
124
Real log_prod = 0 ;
113
- for (size_t i = 1 ; i < b_. size () ; ++i) {
114
- if (b_[i] < static_cast <Z >(logs.size ())) {
125
+ for (size_t i = 1 ; i < size_ ; ++i) {
126
+ if (b_[i] < static_cast <int_type >(logs.size ())) {
115
127
log_prod += logs[b_[i]];
116
128
}
117
129
else
118
130
{
119
131
log_prod += log (static_cast <Real>(b_[i]));
120
132
}
121
133
}
122
- log_prod /= (b_. size () -1 );
134
+ log_prod /= (size_ -1 );
123
135
return exp (log_prod);
124
136
}
125
-
137
+
126
138
Real khinchin_harmonic_mean () const {
127
- if (b_.size () == 1 ) {
139
+ const size_t size_ = b_.size ();
140
+ if (size_ == 1 ) {
128
141
return std::numeric_limits<Real>::quiet_NaN ();
129
142
}
130
- Real n = b_. size () - 1 ;
143
+ Real n = size_ - 1 ;
131
144
Real denom = 0 ;
132
- for (size_t i = 1 ; i < b_. size () ; ++i) {
145
+ for (size_t i = 1 ; i < size_ ; ++i) {
133
146
denom += 1 /static_cast <Real>(b_[i]);
134
147
}
135
148
return n/denom;
136
149
}
137
-
138
- const std::vector<Z>& partial_denominators () const {
150
+
151
+ // Note that this also includes the integer part (i.e. all the coefficients)
152
+ const std::vector<int_type>& partial_denominators () const {
139
153
return b_;
140
154
}
141
-
155
+
142
156
template <typename T, typename Z2>
143
157
friend std::ostream& operator <<(std::ostream& out, simple_continued_fraction<T, Z2>& scf);
158
+ protected:
159
+ simple_continued_fraction () = default ;
144
160
161
+ // Note that non-const operations may result in invalid simple continued fraction
162
+ std::vector<int_type>& partial_denominators () {
163
+ return b_;
164
+ }
145
165
private:
146
- const Real x_;
147
- std::vector<Z> b_;
166
+ std::vector<int_type> b_{};
167
+
168
+ int precision_{};
148
169
};
149
170
150
171
151
172
template <typename Real, typename Z2>
152
173
std::ostream& operator <<(std::ostream& out, simple_continued_fraction<Real, Z2>& scf) {
153
- 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
-
174
+ out << std::setprecision (scf.precision_ );
160
175
out << " [" << scf.b_ .front ();
161
176
if (scf.b_ .size () > 1 )
162
177
{
0 commit comments