CNL  2.0.2 (development)
Compositional Numeric Library
make_fraction.h
1 
2 // Copyright John McFarlane 2015 - 2018.
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file ../LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 
7 #if !defined(CNL_IMPL_FRACTION_MAKE_FRACTION_H)
8 #define CNL_IMPL_FRACTION_MAKE_FRACTION_H
9 
10 #include "../cnl_assert.h"
11 #include "../numbers/set_signedness.h"
12 #include "definition.h"
13 
14 #include <concepts>
15 #include <limits>
16 #include <type_traits>
17 
19 namespace cnl {
20  namespace _impl {
22  template<typename Numerator, typename Denominator>
23  [[nodiscard]] constexpr auto make_fraction(
24  Numerator const& n, Denominator const& d)
25  {
26  return fraction<Numerator, Denominator>{n, d};
27  }
28 
30  template<typename Numerator>
31  [[nodiscard]] constexpr auto make_fraction(Numerator const& n)
32  {
33  return fraction<Numerator, Numerator>{n, 1};
34  }
35 
37  template<
38  typename Numerator, typename Denominator = Numerator,
39  std::floating_point FloatingPoint = float>
40  [[nodiscard]] constexpr auto make_fraction(FloatingPoint d) -> cnl::fraction<Numerator, Denominator>
41  {
42  // merge requests gratefully received
43  static_assert(
45  "This function only supports cnl::fraction<T, T> and not cnl::fraction<T1, "
46  "T2>.");
47 
48  using int_t = Numerator;
49  static_assert(
50  numbers::signedness_v<int_t>,
51  "This function only supports cnl::fraction of signed integers.");
52 
53  using uint_t = numbers::set_signedness_t<int_t, false>;
54 
55  if (d < FloatingPoint{}) {
56  return cnl::fraction<Numerator, Denominator>(-make_fraction<int_t>(-d));
57  }
58  CNL_ASSERT(d <= FloatingPoint(std::numeric_limits<int_t>::max()));
59  auto left{fraction<int_t>(int_t(d), 1)};
60  auto right{fraction<int_t>{int_t(left.numerator + 1), 1}};
61  if (FloatingPoint(left) == d) {
62  return left;
63  }
64  if (FloatingPoint(right) == d) {
65  return right;
66  }
67  auto lefts{0};
68  auto rights{0};
69  for (;;) {
70  auto const mid{fraction<uint_t>(
71  uint_t(left.numerator + right.numerator),
72  uint_t(left.denominator + right.denominator))};
73  CNL_ASSERT(int_t(mid.numerator) >= 0);
74  CNL_ASSERT(int_t(mid.denominator) >= 0);
75 
76  auto fn = [mid, d](int& fars, fraction<int_t>& f, int& nears, fraction<int_t>& n) {
77  nears = 0;
78  if (fars++ < 3) {
79  f = mid;
80  return false;
81  }
82  auto const dividend{
83  d * FloatingPoint(f.denominator) - FloatingPoint(f.numerator)};
84  auto const divisor{
85  FloatingPoint(n.numerator) - d * FloatingPoint(n.denominator)};
86  auto const n0{dividend / divisor};
87  CNL_ASSERT(n0 <= FloatingPoint(std::numeric_limits<int_t>::max()));
88 
89  auto const n1{
90  ((FloatingPoint(f.denominator) + FloatingPoint(n.denominator) * n0)
91  > FloatingPoint(std::numeric_limits<int_t>::max()))
92  ? int_t(FloatingPoint(
94  - f.denominator)
95  / FloatingPoint(n.denominator))
96  : int_t(n0)};
97  auto const n2{
98  ((FloatingPoint(f.numerator) + FloatingPoint(n.numerator * n1))
99  > FloatingPoint(std::numeric_limits<int_t>::max()))
100  ? int_t(FloatingPoint(
101  std::numeric_limits<int_t>::max() - f.numerator
102  - n.numerator)
103  / FloatingPoint(n.numerator))
104  : n1};
105  if (!n2) {
106  return true;
107  }
108  f.numerator = int_t(f.numerator + n2 * n.numerator);
109  f.denominator = int_t(f.denominator + n2 * n.denominator);
110  return FloatingPoint(f) == d;
111  };
112 
113  auto mid_q{FloatingPoint(mid)};
114  if (mid_q < d) {
115  if (fn(lefts, left, rights, right)) {
116  return left;
117  }
118  } else if (mid_q > d) {
119  if (fn(rights, right, lefts, left)) {
120  return right;
121  }
122  } else {
123  return mid;
124  }
125  }
126  }
127  }
128 }
129 
130 #endif // CNL_IMPL_FRACTION_MAKE_FRACTION_H
std::is_same
cnl::fraction
numeric type represented as the fraction, numerator / denominator
Definition: definition.h:28
cnl
compositional numeric library
Definition: abort.h:15
std::left
T left(T... args)
std::numeric_limits