@@ -35,14 +35,25 @@ namespace boost::math::tools {
3535template <typename Real, typename Z = int64_t >
3636class simple_continued_fraction {
3737public:
38- simple_continued_fraction (Real x) : x_{x} {
38+ typedef Z int_type;
39+
40+ simple_continued_fraction (Real x) {
3941 using std::floor;
4042 using std::abs;
4143 using std::sqrt;
4244 using std::isfinite;
45+ const Real orig_x = x;
4346 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;
4555 }
56+
4657 b_.reserve (50 );
4758 Real bj = floor (x);
4859 b_.push_back (static_cast <Z>(bj));
@@ -57,12 +68,11 @@ class simple_continued_fraction {
5768 }
5869 Real C = f;
5970 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.
6272 // I have not worked out the error propagation of the Modified Lentz's method to see if it does indeed grow at this rate.
6373 // 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) {
6676 bj = floor (x);
6777 b_.push_back (static_cast <Z>(bj));
6878 x = 1 /(x-bj);
@@ -81,12 +91,13 @@ class simple_continued_fraction {
8191 // The shorter representation is considered the canonical representation,
8292 // so if we compute a non-canonical representation, change it to canonical:
8393 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 ;
8696 }
8797 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) {
90101 if (b_[i] <= 0 ) {
91102 std::ostringstream oss;
92103 oss << " Found a negative partial denominator: b[" << i << " ] = " << b_[i] << " ."
@@ -101,9 +112,10 @@ class simple_continued_fraction {
101112 }
102113 }
103114 }
104-
115+
105116 Real khinchin_geometric_mean () const {
106- if (b_.size () == 1 ) {
117+ const size_t size_ = b_.size ();
118+ if (size_ == 1 ) {
107119 return std::numeric_limits<Real>::quiet_NaN ();
108120 }
109121 using std::log;
@@ -113,7 +125,7 @@ class simple_continued_fraction {
113125 // A random partial denominator has ~80% chance of being in this table:
114126 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 ))};
115127 Real log_prod = 0 ;
116- for (size_t i = 1 ; i < b_. size () ; ++i) {
128+ for (size_t i = 1 ; i < size_ ; ++i) {
117129 if (b_[i] < static_cast <Z>(logs.size ())) {
118130 log_prod += logs[b_[i]];
119131 }
@@ -122,49 +134,44 @@ class simple_continued_fraction {
122134 log_prod += log (static_cast <Real>(b_[i]));
123135 }
124136 }
125- log_prod /= (b_. size () -1 );
137+ log_prod /= (size_ -1 );
126138 return exp (log_prod);
127139 }
128-
140+
129141 Real khinchin_harmonic_mean () const {
130- if (b_.size () == 1 ) {
142+ const size_t size_ = b_.size ();
143+ if (size_ == 1 ) {
131144 return std::numeric_limits<Real>::quiet_NaN ();
132145 }
133- Real n = b_. size () - 1 ;
146+ Real n = size_ - 1 ;
134147 Real denom = 0 ;
135- for (size_t i = 1 ; i < b_. size () ; ++i) {
148+ for (size_t i = 1 ; i < size_ ; ++i) {
136149 denom += 1 /static_cast <Real>(b_[i]);
137150 }
138151 return n/denom;
139152 }
140-
153+
154+ // Note that this also includes the integer part (i.e. all the coefficients)
141155 const std::vector<Z>& partial_denominators () const {
142156 return b_;
143157 }
144158
145- inline std::vector<Z>&& get_data() noexcept
146- {
159+ inline std::vector<Z>&& get_data() noexcept {
147160 return std::move (b_);
148161 }
149-
162+
150163 template <typename T, typename Z2>
151164 friend std::ostream& operator <<(std::ostream& out, simple_continued_fraction<T, Z2>& scf);
152-
153165private:
154- const Real x_;
155- std::vector<Z> b_;
166+ std::vector<Z> b_{};
167+
168+ int precision_{};
156169};
157170
158171
159172template <typename Real, typename Z2>
160173std::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_ );
168175 out << " [" << scf.b_ .front ();
169176 if (scf.b_ .size () > 1 )
170177 {
0 commit comments