@@ -32,17 +32,28 @@ 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." );
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;
4252 }
53+
4354 b_.reserve (50 );
4455 Real bj = floor (x);
45- b_.push_back (static_cast <Z >(bj));
56+ b_.push_back (static_cast <int_type >(bj));
4657 if (bj == x) {
4758 b_.shrink_to_fit ();
4859 return ;
@@ -54,14 +65,13 @@ class simple_continued_fraction {
5465 }
5566 Real C = f;
5667 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.
5969 // I have not worked out the error propagation of the Modified Lentz's method to see if it does indeed grow at this rate.
6070 // 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) {
6373 bj = floor (x);
64- b_.push_back (static_cast <Z >(bj));
74+ b_.push_back (static_cast <int_type >(bj));
6575 x = 1 /(x-bj);
6676 D += bj;
6777 if (D == 0 ) {
@@ -78,29 +88,31 @@ class simple_continued_fraction {
7888 // The shorter representation is considered the canonical representation,
7989 // so if we compute a non-canonical representation, change it to canonical:
8090 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 ;
8393 }
8494 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) {
8798 if (b_[i] <= 0 ) {
8899 std::ostringstream oss;
89100 oss << " Found a negative partial denominator: b[" << i << " ] = " << b_[i] << " ."
90101 #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 ())
92103 #else
93- << " This means the integer type '" << typeid (Z ).name ()
104+ << " This means the integer type '" << typeid (int_type ).name ()
94105 #endif
95106 << " ' has overflowed and you need to use a wider type,"
96107 << " or there is a bug." ;
97108 throw std::overflow_error (oss.str ());
98109 }
99110 }
100111 }
101-
112+
102113 Real khinchin_geometric_mean () const {
103- if (b_.size () == 1 ) {
114+ const size_t size_ = b_.size ();
115+ if (size_ == 1 ) {
104116 return std::numeric_limits<Real>::quiet_NaN ();
105117 }
106118 using std::log;
@@ -110,53 +122,56 @@ class simple_continued_fraction {
110122 // A random partial denominator has ~80% chance of being in this table:
111123 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 ))};
112124 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 ())) {
115127 log_prod += logs[b_[i]];
116128 }
117129 else
118130 {
119131 log_prod += log (static_cast <Real>(b_[i]));
120132 }
121133 }
122- log_prod /= (b_. size () -1 );
134+ log_prod /= (size_ -1 );
123135 return exp (log_prod);
124136 }
125-
137+
126138 Real khinchin_harmonic_mean () const {
127- if (b_.size () == 1 ) {
139+ const size_t size_ = b_.size ();
140+ if (size_ == 1 ) {
128141 return std::numeric_limits<Real>::quiet_NaN ();
129142 }
130- Real n = b_. size () - 1 ;
143+ Real n = size_ - 1 ;
131144 Real denom = 0 ;
132- for (size_t i = 1 ; i < b_. size () ; ++i) {
145+ for (size_t i = 1 ; i < size_ ; ++i) {
133146 denom += 1 /static_cast <Real>(b_[i]);
134147 }
135148 return n/denom;
136149 }
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 {
139153 return b_;
140154 }
141-
155+
142156 template <typename T, typename Z2>
143157 friend std::ostream& operator <<(std::ostream& out, simple_continued_fraction<T, Z2>& scf);
158+ protected:
159+ simple_continued_fraction () = default ;
144160
161+ // Note that non-const operations may result in invalid simple continued fraction
162+ std::vector<int_type>& partial_denominators () {
163+ return b_;
164+ }
145165private:
146- const Real x_;
147- std::vector<Z> b_;
166+ std::vector<int_type> b_{};
167+
168+ int precision_{};
148169};
149170
150171
151172template <typename Real, typename Z2>
152173std::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_ );
160175 out << " [" << scf.b_ .front ();
161176 if (scf.b_ .size () > 1 )
162177 {
0 commit comments