Skip to content

Commit 0101051

Browse files
author
Tomáš Kolárik
committed
simple_continued_fraction: added protected non-const accessor to partial denominators (#970)
1 parent 5376689 commit 0101051

File tree

1 file changed

+50
-35
lines changed

1 file changed

+50
-35
lines changed

include/boost/math/tools/simple_continued_fraction.hpp

+50-35
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,28 @@ namespace boost::math::tools {
3232
template<typename Real, typename Z = int64_t>
3333
class simple_continued_fraction {
3434
public:
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+
}
145165
private:
146-
const Real x_;
147-
std::vector<Z> b_;
166+
std::vector<int_type> b_{};
167+
168+
int precision_{};
148169
};
149170

150171

151172
template<typename Real, typename Z2>
152173
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_);
160175
out << "[" << scf.b_.front();
161176
if (scf.b_.size() > 1)
162177
{

0 commit comments

Comments
 (0)