aGrUM 3.1.1
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<GUM_Numeric 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<GUM_Numeric 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 218 of file rational_tpl.h.

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

◆ continuedFracFirst()

template<GUM_Numeric 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 121 of file rational_tpl.h.

124 {
125 const GUM_SCALAR pnumber = (number > 0) ? number : -number;
126
129
133
136
138
139 uint64_t n;
140 double delta, delta_tmp;
141
147 while (true) {
148 a.push_back(std::lrint(std::floor(rnumber)));
149 p.push_back(a.back() * p.back() + p[p.size() - 2]);
150 q.push_back(a.back() * q.back() + q[q.size() - 2]);
151
152 delta = std::fabs(pnumber - (GUM_SCALAR)p.back() / q.back());
153
154 if (delta < zero) {
155 numerator = (int64_t)p.back();
156 if (number < 0) numerator = -numerator;
157 denominator = q.back();
158 break;
159 }
160
161 if (std::abs(rnumber - a.back()) < 1e-6) break;
162
163 rnumber = GUM_SCALAR(1.) / (rnumber - a.back());
164 }
165
166 if (a.size() < 2) return;
167
171 Idx i = Idx(p.size() - 2);
175 // Test n = a[i-1]/2 ( when a[i-1] is even )
176 n = a[i - 1] / 2;
177 p_tmp = n * p[i] + p[i - 1];
178 q_tmp = n * q[i] + q[i - 1];
179
180 delta = std::fabs(pnumber - ((double)p[i]) / q[i]);
181 delta_tmp = std::fabs(pnumber - ((double)p_tmp) / q_tmp);
182
183 if (delta < zero) {
184 numerator = (int64_t)p[i];
185 if (number < 0) numerator = -numerator;
186 denominator = q[i];
187 return;
188 }
189
190 if (delta_tmp < zero) {
192 if (number < 0) numerator = -numerator;
194 return;
195 }
196
197 // next semi-convergents until next convergent from smaller denominator to
198 // bigger
199 // denominator
200 for (n = (a[i - 1] + 2) / 2; n < a[i - 1]; ++n) {
201 p_tmp = n * p[i] + p[i - 1];
202 q_tmp = n * q[i] + q[i - 1];
203
204 delta_tmp = std::fabs(pnumber - ((double)p_tmp) / q_tmp);
205
206 if (delta_tmp < zero) {
208 if (number < 0) numerator = -numerator;
210 return;
211 }
212 }
213
215 }

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

Here is the caller graph for this function:

◆ farey()

template<GUM_Numeric 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 58 of file rational_tpl.h.

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

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: