@@ -32,17 +32,20 @@ namespace boost::math::tools {
3232template <typename Real, typename Z = int64_t >
3333class simple_continued_fraction {
3434public:
35- simple_continued_fraction (Real x) : x_{x} {
35+ typedef Z int_type;
36+
37+ simple_continued_fraction (Real x) {
3638 using std::floor;
3739 using std::abs;
3840 using std::sqrt;
3941 using std::isfinite;
42+ const Real orig_x = x;
4043 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." );
4245 }
4346 b_.reserve (50 );
4447 Real bj = floor (x);
45- b_.push_back (static_cast <Z >(bj));
48+ b_.push_back (static_cast <int_type >(bj));
4649 if (bj == x) {
4750 b_.shrink_to_fit ();
4851 return ;
@@ -54,14 +57,13 @@ class simple_continued_fraction {
5457 }
5558 Real C = f;
5659 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.
5961 // I have not worked out the error propagation of the Modified Lentz's method to see if it does indeed grow at this rate.
6062 // 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) {
6365 bj = floor (x);
64- b_.push_back (static_cast <Z >(bj));
66+ b_.push_back (static_cast <int_type >(bj));
6567 x = 1 /(x-bj);
6668 D += bj;
6769 if (D == 0 ) {
@@ -78,29 +80,31 @@ class simple_continued_fraction {
7880 // The shorter representation is considered the canonical representation,
7981 // so if we compute a non-canonical representation, change it to canonical:
8082 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 ;
8385 }
8486 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) {
8790 if (b_[i] <= 0 ) {
8891 std::ostringstream oss;
8992 oss << " Found a negative partial denominator: b[" << i << " ] = " << b_[i] << " ."
9093 #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 ())
9295 #else
93- << " This means the integer type '" << typeid (Z ).name ()
96+ << " This means the integer type '" << typeid (int_type ).name ()
9497 #endif
9598 << " ' has overflowed and you need to use a wider type,"
9699 << " or there is a bug." ;
97100 throw std::overflow_error (oss.str ());
98101 }
99102 }
100103 }
101-
104+
102105 Real khinchin_geometric_mean () const {
103- if (b_.size () == 1 ) {
106+ const size_t size_ = b_.size ();
107+ if (size_ == 1 ) {
104108 return std::numeric_limits<Real>::quiet_NaN ();
105109 }
106110 using std::log;
@@ -110,53 +114,57 @@ class simple_continued_fraction {
110114 // A random partial denominator has ~80% chance of being in this table:
111115 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 ))};
112116 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 ())) {
115119 log_prod += logs[b_[i]];
116120 }
117121 else
118122 {
119123 log_prod += log (static_cast <Real>(b_[i]));
120124 }
121125 }
122- log_prod /= (b_. size () -1 );
126+ log_prod /= (size_ -1 );
123127 return exp (log_prod);
124128 }
125-
129+
126130 Real khinchin_harmonic_mean () const {
127- if (b_.size () == 1 ) {
131+ const size_t size_ = b_.size ();
132+ if (size_ == 1 ) {
128133 return std::numeric_limits<Real>::quiet_NaN ();
129134 }
130- Real n = b_. size () - 1 ;
135+ Real n = size_ - 1 ;
131136 Real denom = 0 ;
132- for (size_t i = 1 ; i < b_. size () ; ++i) {
137+ for (size_t i = 1 ; i < size_ ; ++i) {
133138 denom += 1 /static_cast <Real>(b_[i]);
134139 }
135140 return n/denom;
136141 }
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 {
139145 return b_;
140146 }
141-
147+
142148 template <typename T, typename Z2>
143149 friend std::ostream& operator <<(std::ostream& out, simple_continued_fraction<T, Z2>& scf);
150+ protected:
151+ simple_continued_fraction () = default ;
144152
153+ // Note that non-const operations may result in invalid simple continued fraction
154+ std::vector<int_type>& partial_denominators () {
155+ return b_;
156+ }
145157private:
146- const Real x_;
147- std::vector<Z> b_;
158+ std::vector<int_type> b_{};
148159};
149160
150161
151162template <typename Real, typename Z2>
152163std::ostream& operator <<(std::ostream& out, simple_continued_fraction<Real, Z2>& scf) {
153164 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+
160168 out << " [" << scf.b_ .front ();
161169 if (scf.b_ .size () > 1 )
162170 {
0 commit comments