aGrUM 2.3.2
a C++ library for (probabilistic) graphical models
gum::Rational< GUM_SCALAR > Class Template Reference

Class template used to approximate decimal numbers by rationals. More...

#include <agrum/base/core/math/rational.h>

Static Public Member Functions

Real approximation by rational
static void farey (int64_t &numerator, int64_t &denominator, const GUM_SCALAR &number, const int64_t &den_max=1000000L, const GUM_SCALAR &zero=1e-6)
 Find the rational close enough to a given ( decimal ) number in [-1,1] and whose denominator is not higher than a given integer number.
static void continuedFracFirst (int64_t &numerator, int64_t &denominator, const GUM_SCALAR &number, const double &zero=1e-6)
 Find the first best rational approximation.
static void continuedFracBest (int64_t &numerator, int64_t &denominator, const GUM_SCALAR &number, const int64_t &den_max=1000000)
 Find the best rational approximation.

Detailed Description

template<typename GUM_SCALAR>
class gum::Rational< GUM_SCALAR >

Class template used to approximate decimal numbers by rationals.

Template Parameters
GUM_SCALARThe floating type ( float, double, long double ... ) of the number.

Definition at line 79 of file rational.h.

Member Function Documentation

◆ continuedFracBest()

template<typename GUM_SCALAR>
void gum::Rational< GUM_SCALAR >::continuedFracBest ( int64_t & numerator,
int64_t & denominator,
const GUM_SCALAR & number,
const int64_t & den_max = 1000000 )
static

Find the best rational approximation.

Not the first, to a given ( decimal) number ( ANY number ) and whose denominator is not higher than a given integer number.

In this case, we look for semi-convergents at the right of the last admissible convergent, if any. They are better approximations, but have higher denominators.

Parameters
numeratorThe numerator of the rational.
denominatorThe denominator of the rational.
numberThe constant number we want to approximate using rationals.
den_maxThe constant highest authorized denominator. 1000000 by default.

Definition at line 213 of file rational_tpl.h.

216 {
217 const GUM_SCALAR pnumber = (number > 0) ? number : -number;
218
220
223
227
230
232
233 uint64_t n;
234 double delta, delta_tmp;
235
237 while (true) {
238 a.push_back(std::lrint(std::floor(rnumber)));
239
240 p_tmp = a.back() * p.back() + p[p.size() - 2];
241 q_tmp = a.back() * q.back() + q[q.size() - 2];
242
243 if (q_tmp > denMax || p_tmp > denMax) break;
244
245 p.push_back(p_tmp);
246 q.push_back(q_tmp);
247
248 if (std::fabs(rnumber - a.back()) < 1e-6) break;
249
250 rnumber = GUM_SCALAR(1.) / (rnumber - a.back());
251 }
252
253 if (a.size() < 2 || q.back() == denMax || p.back() == denMax) {
254 numerator = p.back();
255 if (number < 0) numerator = -numerator;
256 denominator = q.back();
257 return;
258 }
259
263 Idx i = Idx(p.size() - 1);
264
268 for (n = a[i - 1] - 1; n >= (a[i - 1] + 2) / 2; --n) {
269 p_tmp = n * p[i] + p[i - 1];
270 q_tmp = n * q[i] + q[i - 1];
271
272 if (q_tmp > denMax || p_tmp > denMax) continue;
273
275 if (number < 0) numerator = -numerator;
277 return;
278 } // end of for
279
280 // Test n = a[i-1]/2
281 n = a[i - 1] / 2;
282 p_tmp = n * p[i] + p[i - 1];
283 q_tmp = n * q[i] + q[i - 1];
284
285 delta_tmp = std::fabs(pnumber - ((double)p_tmp) / q_tmp);
286 delta = std::fabs(pnumber - ((double)p[i]) / q[i]);
287
288 if (delta_tmp < delta && q_tmp <= denMax && p_tmp <= denMax) {
290 if (number < 0) numerator = -numerator;
292 } else {
293 numerator = (int64_t)p[i];
294 if (number < 0) numerator = -numerator;
295
296 denominator = q[i];
297 }
298
300 }
Class template used to approximate decimal numbers by rationals.
Definition rational.h:79
Size Idx
Type for indexes.
Definition types.h:79

◆ continuedFracFirst()

template<typename GUM_SCALAR>
void gum::Rational< GUM_SCALAR >::continuedFracFirst ( int64_t & numerator,
int64_t & denominator,
const GUM_SCALAR & number,
const double & zero = 1e-6 )
static

Find the first best rational approximation.

end of farey func

The one with the smallest denominator such that no other rational with smaller denominator is a better approx, within precision zero to a given ( decimal ) number ( ANY number).

It gives the same answer than farey assuming zero is the same and den_max is infinite. Use this functions because you are sure to get an approx within zero of number.

We look at the semi-convergents left of the last admissible convergent, if any. They may be within the same precision and have a smaller denominator.

Parameters
numeratorThe numerator of the rational.
denominatorThe denominator of the rational.
numberThe constant number we want to approximate using rationals.
zeroThe positive value below which a number is considered zero. 1e-6 by default.

Definition at line 116 of file rational_tpl.h.

119 {
120 const GUM_SCALAR pnumber = (number > 0) ? number : -number;
121
124
128
131
133
134 uint64_t n;
135 double delta, delta_tmp;
136
142 while (true) {
143 a.push_back(std::lrint(std::floor(rnumber)));
144 p.push_back(a.back() * p.back() + p[p.size() - 2]);
145 q.push_back(a.back() * q.back() + q[q.size() - 2]);
146
147 delta = std::fabs(pnumber - (GUM_SCALAR)p.back() / q.back());
148
149 if (delta < zero) {
150 numerator = (int64_t)p.back();
151 if (number < 0) numerator = -numerator;
152 denominator = q.back();
153 break;
154 }
155
156 if (std::abs(rnumber - a.back()) < 1e-6) break;
157
158 rnumber = GUM_SCALAR(1.) / (rnumber - a.back());
159 }
160
161 if (a.size() < 2) return;
162
166 Idx i = Idx(p.size() - 2);
170 // Test n = a[i-1]/2 ( when a[i-1] is even )
171 n = a[i - 1] / 2;
172 p_tmp = n * p[i] + p[i - 1];
173 q_tmp = n * q[i] + q[i - 1];
174
175 delta = std::fabs(pnumber - ((double)p[i]) / q[i]);
176 delta_tmp = std::fabs(pnumber - ((double)p_tmp) / q_tmp);
177
178 if (delta < zero) {
179 numerator = (int64_t)p[i];
180 if (number < 0) numerator = -numerator;
181 denominator = q[i];
182 return;
183 }
184
185 if (delta_tmp < zero) {
187 if (number < 0) numerator = -numerator;
189 return;
190 }
191
192 // next semi-convergents until next convergent from smaller denominator to
193 // bigger
194 // denominator
195 for (n = (a[i - 1] + 2) / 2; n < a[i - 1]; ++n) {
196 p_tmp = n * p[i] + p[i - 1];
197 q_tmp = n * q[i] + q[i - 1];
198
199 delta_tmp = std::fabs(pnumber - ((double)p_tmp) / q_tmp);
200
201 if (delta_tmp < zero) {
203 if (number < 0) numerator = -numerator;
205 return;
206 }
207 }
208
210 }

Referenced by gum::credal::LRSWrapper< GUM_SCALAR >::_fill_().

Here is the caller graph for this function:

◆ farey()

template<typename GUM_SCALAR>
void gum::Rational< GUM_SCALAR >::farey ( int64_t & numerator,
int64_t & denominator,
const GUM_SCALAR & number,
const int64_t & den_max = 1000000L,
const GUM_SCALAR & zero = 1e-6 )
static

Find the rational close enough to a given ( decimal ) number in [-1,1] and whose denominator is not higher than a given integer number.

Because of the double constraint on precision and size of the denominator, there is no guarantee on the precision of the approximation if den_max is low and zero is high. Prefer the use of continued fractions.

Parameters
numeratorThe numerator of the rational.
denominatorThe denominator of the rational.
numberThe constant number we want to approximate using rationals.
den_maxThe constant highest authorized denominator. 1000000 by default.
zeroThe positive value below which a number is considered zero. 1e-6 by default.

Definition at line 57 of file rational_tpl.h.

61 {
62 bool isNegative = (number < 0) ? true : false;
64
65 if (std::abs(pnumber - GUM_SCALAR(1.)) < zero) {
66 numerator = (isNegative) ? -1 : 1;
67 denominator = 1;
68 return;
69 } else if (pnumber < zero) {
70 numerator = 0;
71 denominator = 1;
72 return;
73 }
74
75 int64_t a(0), b(1), c(1), d(1);
76 double mediant(0.0F);
77
78 while (b <= den_max && d <= den_max) {
79 mediant = (GUM_SCALAR)(a + c) / (GUM_SCALAR)(b + d);
80
81 if (std::fabs(pnumber - mediant) < zero) {
82 if (b + d <= den_max) {
83 numerator = (isNegative) ? -(a + c) : (a + c);
84 denominator = b + d;
85 return;
86 } else if (d > b) {
87 numerator = (isNegative) ? -c : c;
88 denominator = d;
89 return;
90 } else {
91 numerator = (isNegative) ? -a : a;
92 denominator = b;
93 return;
94 }
95 } else if (pnumber > mediant) {
96 a = a + c;
97 b = b + d;
98 } else {
99 c = a + c;
100 d = b + d;
101 }
102 }
103
104 if (b > den_max) {
105 numerator = (isNegative) ? -c : c;
106 denominator = d;
107 return;
108 } else {
109 numerator = (isNegative) ? -a : a;
110 denominator = b;
111 return;
112 }
113 }

Referenced by gum::credal::CredalNet< GUM_SCALAR >::_H2Vlrs_().

Here is the caller graph for this function:

The documentation for this class was generated from the following files: