aGrUM  0.20.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/tools/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. More...
 
static void continuedFracFirst (int64_t &numerator, int64_t &denominator, const GUM_SCALAR &number, const double &zero=1e-6)
 Find the first best rational approximation. More...
 
static void continuedFracBest (int64_t &numerator, int64_t &denominator, const GUM_SCALAR &number, const int64_t &den_max=1000000)
 Find the best rational approximation. More...
 

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

References gum::Set< Key, Alloc >::emplace().

194  {
195  const GUM_SCALAR pnumber = (number > 0) ? number : -number;
196 
197  const uint64_t denMax
198  = (uint64_t)den_max;
199 
201  GUM_SCALAR rnumber = pnumber;
202 
204  std::vector< uint64_t > p({0, 1});
205  std::vector< uint64_t > q({1, 0});
206 
208  std::vector< uint64_t > a;
209 
210  uint64_t p_tmp, q_tmp;
211 
212  uint64_t n;
213  double delta, delta_tmp;
214 
216  while (true) {
217  a.push_back(std::lrint(std::floor(rnumber)));
218 
219  p_tmp = a.back() * p.back() + p[p.size() - 2];
220  q_tmp = a.back() * q.back() + q[q.size() - 2];
221 
222  if (q_tmp > denMax || p_tmp > denMax) break;
223 
224  p.push_back(p_tmp);
225  q.push_back(q_tmp);
226 
227  if (std::fabs(rnumber - a.back()) < 1e-6) break;
228 
229  rnumber = GUM_SCALAR(1.) / (rnumber - a.back());
230  }
231 
232  if (a.size() < 2 || q.back() == denMax || p.back() == denMax) {
233  numerator = p.back();
234  if (number < 0) numerator = -numerator;
235  denominator = q.back();
236  return;
237  }
238 
242  Idx i = Idx(p.size() - 1);
243 
247  for (n = a[i - 1] - 1; n >= (a[i - 1] + 2) / 2; --n) {
248  p_tmp = n * p[i] + p[i - 1];
249  q_tmp = n * q[i] + q[i - 1];
250 
251  if (q_tmp > denMax || p_tmp > denMax) continue;
252 
253  numerator = (int64_t)p_tmp;
254  if (number < 0) numerator = -numerator;
255  denominator = q_tmp;
256  return;
257  } // end of for
258 
259  // Test n = a[i-1]/2
260  n = a[i - 1] / 2;
261  p_tmp = n * p[i] + p[i - 1];
262  q_tmp = n * q[i] + q[i - 1];
263 
264  delta_tmp = std::fabs(pnumber - ((double)p_tmp) / q_tmp);
265  delta = std::fabs(pnumber - ((double)p[i]) / q[i]);
266 
267  if (delta_tmp < delta && q_tmp <= denMax && p_tmp <= denMax) {
268  numerator = (int64_t)p_tmp;
269  if (number < 0) numerator = -numerator;
270  denominator = q_tmp;
271  } else {
272  numerator = (int64_t)p[i];
273  if (number < 0) numerator = -numerator;
274 
275  denominator = q[i];
276  }
277 
279  }
Size Idx
Type for indexes.
Definition: types.h:52
+ Here is the call graph for this function:

◆ 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 94 of file rational_tpl.h.

References gum::Set< Key, Alloc >::emplace().

97  {
98  const GUM_SCALAR pnumber = (number > 0) ? number : -number;
99 
101  GUM_SCALAR rnumber = pnumber;
102 
104  std::vector< uint64_t > p({0, 1});
105  std::vector< uint64_t > q({1, 0});
106 
108  std::vector< uint64_t > a;
109 
110  uint64_t p_tmp, q_tmp;
111 
112  uint64_t n;
113  double delta, delta_tmp;
114 
120  while (true) {
121  a.push_back(std::lrint(std::floor(rnumber)));
122  p.push_back(a.back() * p.back() + p[p.size() - 2]);
123  q.push_back(a.back() * q.back() + q[q.size() - 2]);
124 
125  delta = std::fabs(pnumber - (GUM_SCALAR)p.back() / q.back());
126 
127  if (delta < zero) {
128  numerator = (int64_t)p.back();
129  if (number < 0) numerator = -numerator;
130  denominator = q.back();
131  break;
132  }
133 
134  if (std::abs(rnumber - a.back()) < 1e-6) break;
135 
136  rnumber = GUM_SCALAR(1.) / (rnumber - a.back());
137  }
138 
139  if (a.size() < 2) return;
140 
144  Idx i = Idx(p.size() - 2);
148  // Test n = a[i-1]/2 ( when a[i-1] is even )
149  n = a[i - 1] / 2;
150  p_tmp = n * p[i] + p[i - 1];
151  q_tmp = n * q[i] + q[i - 1];
152 
153  delta = std::fabs(pnumber - ((double)p[i]) / q[i]);
154  delta_tmp = std::fabs(pnumber - ((double)p_tmp) / q_tmp);
155 
156  if (delta < zero) {
157  numerator = (int64_t)p[i];
158  if (number < 0) numerator = -numerator;
159  denominator = q[i];
160  return;
161  }
162 
163  if (delta_tmp < zero) {
164  numerator = (int64_t)p_tmp;
165  if (number < 0) numerator = -numerator;
166  denominator = q_tmp;
167  return;
168  }
169 
170  // next semi-convergents until next convergent from smaller denominator to
171  // bigger
172  // denominator
173  for (n = (a[i - 1] + 2) / 2; n < a[i - 1]; ++n) {
174  p_tmp = n * p[i] + p[i - 1];
175  q_tmp = n * q[i] + q[i - 1];
176 
177  delta_tmp = std::fabs(pnumber - ((double)p_tmp) / q_tmp);
178 
179  if (delta_tmp < zero) {
180  numerator = (int64_t)p_tmp;
181  if (number < 0) numerator = -numerator;
182  denominator = q_tmp;
183  return;
184  }
185  }
186 
188  }
Potential< GUM_SCALAR > abs(const Potential< GUM_SCALAR > &arg)
Definition: potential.h:617
Size Idx
Type for indexes.
Definition: types.h:52
+ Here is the call 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 35 of file rational_tpl.h.

References gum::Set< Key, Alloc >::emplace().

39  {
40  bool isNegative = (number < 0) ? true : false;
41  GUM_SCALAR pnumber = (isNegative) ? -number : number;
42 
43  if (std::abs(pnumber - GUM_SCALAR(1.)) < zero) {
44  numerator = (isNegative) ? -1 : 1;
45  denominator = 1;
46  return;
47  } else if (pnumber < zero) {
48  numerator = 0;
49  denominator = 1;
50  return;
51  }
52 
53  int64_t a(0), b(1), c(1), d(1);
54  double mediant(0.0F);
55 
56  while (b <= den_max && d <= den_max) {
57  mediant = (GUM_SCALAR)(a + c) / (GUM_SCALAR)(b + d);
58 
59  if (std::fabs(pnumber - mediant) < zero) {
60  if (b + d <= den_max) {
61  numerator = (isNegative) ? -(a + c) : (a + c);
62  denominator = b + d;
63  return;
64  } else if (d > b) {
65  numerator = (isNegative) ? -c : c;
66  denominator = d;
67  return;
68  } else {
69  numerator = (isNegative) ? -a : a;
70  denominator = b;
71  return;
72  }
73  } else if (pnumber > mediant) {
74  a = a + c;
75  b = b + d;
76  } else {
77  c = a + c;
78  d = b + d;
79  }
80  }
81 
82  if (b > den_max) {
83  numerator = (isNegative) ? -c : c;
84  denominator = d;
85  return;
86  } else {
87  numerator = (isNegative) ? -a : a;
88  denominator = b;
89  return;
90  }
91  }
Potential< GUM_SCALAR > abs(const Potential< GUM_SCALAR > &arg)
Definition: potential.h:617
+ Here is the call graph for this function:

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