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