Skip to content

Commit 360f503

Browse files
Added missing rational arithmetic functions from <ratio> (#1014)
* Added ratio traits * Fix unused test suite in < C++14 case * Port to C++11 * Fix header includes
1 parent 99d7537 commit 360f503

File tree

3 files changed

+297
-3
lines changed

3 files changed

+297
-3
lines changed

include/etl/ratio.h

Lines changed: 152 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ SOFTWARE.
3333

3434
#include "platform.h"
3535

36+
#include "type_traits.h"
37+
3638
#include <stddef.h>
3739
#include <stdint.h>
3840

@@ -41,17 +43,17 @@ SOFTWARE.
4143

4244
namespace etl
4345
{
44-
template <size_t NUM, size_t DEN = 1UL>
46+
template <intmax_t NUM, intmax_t DEN = 1UL>
4547
struct ratio
4648
{
4749
static ETL_CONSTANT intmax_t num = NUM;
4850
static ETL_CONSTANT intmax_t den = DEN;
4951
};
5052

51-
template <size_t NUM, size_t DEN>
53+
template <intmax_t NUM, intmax_t DEN>
5254
ETL_CONSTANT intmax_t ratio<NUM, DEN>::num;
5355

54-
template <size_t NUM, size_t DEN>
56+
template <intmax_t NUM, intmax_t DEN>
5557
ETL_CONSTANT intmax_t ratio<NUM, DEN>::den;
5658

5759
#if INT_MAX > INT32_MAX
@@ -97,6 +99,153 @@ namespace etl
9799

98100
/// An approximation of e.
99101
typedef ratio<326, 120> ratio_e;
102+
103+
#if ETL_USING_CPP11
104+
namespace private_ratio
105+
{
106+
// Primary template for GCD calculation
107+
template <typename T, T A, T B, bool = (B == 0)>
108+
struct ratio_gcd;
109+
110+
// Specialisation for the case when B is not zero
111+
template <typename T, T A, T B>
112+
struct ratio_gcd<T, A, B, false>
113+
{
114+
static constexpr T value = ratio_gcd<T, B, A % B>::value;
115+
};
116+
117+
// Specialisation for the case when B is zero
118+
template <typename T, T A, T B>
119+
struct ratio_gcd<T, A, B, true>
120+
{
121+
static constexpr T value = (A < 0) ? -A : A;
122+
};
123+
124+
// Primary template for LCM calculation
125+
template <typename T, T A, T B>
126+
struct ratio_lcm
127+
{
128+
private:
129+
130+
static constexpr T product = ((A * B) < 0) ? -(A * B) : A * B;
131+
132+
public:
133+
134+
static constexpr T value = product / ratio_gcd<T, A, B>::value;
135+
};
136+
137+
template<typename R1>
138+
struct ratio_reduce
139+
{
140+
private:
141+
142+
static ETL_CONSTEXPR11 intmax_t gcd = etl::private_ratio::ratio_gcd<intmax_t, R1::num, R1::den>::value;
143+
144+
public:
145+
146+
using value = ratio<R1::num / gcd, R1::den / gcd>;
147+
};
148+
149+
template<typename R1, typename R2>
150+
struct ratio_add
151+
{
152+
private:
153+
154+
static ETL_CONSTEXPR11 intmax_t lcm = etl::private_ratio::ratio_lcm<intmax_t, R1::den, R2::den>::value;
155+
156+
public:
157+
158+
using value = typename ratio_reduce<ratio<R1::num * lcm / R1::den + R2::num * lcm / R2::den, lcm>>::value;
159+
};
160+
161+
template<typename R1, typename R2>
162+
struct ratio_subtract
163+
{
164+
public:
165+
using value = typename ratio_add<R1, ratio<-R2::num, R2::den>>::value;
166+
};
167+
168+
template<typename R1, typename R2>
169+
struct ratio_multiply
170+
{
171+
private:
172+
static ETL_CONSTEXPR11 intmax_t gcd1 = etl::private_ratio::ratio_gcd<intmax_t, R1::num, R2::den>::value;
173+
static ETL_CONSTEXPR11 intmax_t gcd2 = etl::private_ratio::ratio_gcd<intmax_t, R2::num, R1::den>::value;
174+
175+
public:
176+
using value = ratio<(R1::num / gcd1) * (R2::num / gcd2), (R1::den / gcd2) * (R2::den / gcd1)>;
177+
};
178+
179+
template<typename R1, typename R2>
180+
struct ratio_divide
181+
{
182+
public:
183+
using value = typename ratio_multiply<R1, ratio<R2::den, R2::num>>::value;
184+
};
185+
}
186+
187+
template<typename R1, typename R2>
188+
using ratio_add = typename private_ratio::ratio_add<R1, R2>::value;
189+
190+
template<typename R1, typename R2>
191+
using ratio_subtract = typename private_ratio::ratio_subtract<R1, R2>::value;
192+
193+
template<typename R1, typename R2>
194+
using ratio_multiply = typename private_ratio::ratio_multiply<R1, R2>::value;
195+
196+
template<typename R1, typename R2>
197+
using ratio_divide = typename private_ratio::ratio_divide<R1, R2>::value;
198+
199+
template<typename R1, typename R2>
200+
struct ratio_equal: etl::integral_constant<bool, (R1::num == R2::num && R1::den == R2::den)>
201+
{
202+
};
203+
204+
template<typename R1, typename R2>
205+
struct ratio_not_equal: etl::integral_constant<bool, (R1::num != R2::num || R1::den != R2::den)>
206+
{
207+
};
208+
209+
template<typename R1, typename R2>
210+
struct ratio_less: etl::integral_constant<bool, (R1::num * R2::den < R2::num * R1::den)>
211+
{
212+
};
213+
214+
template<typename R1, typename R2>
215+
struct ratio_less_equal: etl::integral_constant<bool, (R1::num * R2::den <= R2::num * R1::den)>
216+
{
217+
};
218+
219+
template<typename R1, typename R2>
220+
struct ratio_greater: etl::integral_constant<bool, (R1::num * R2::den > R2::num * R1::den)>
221+
{
222+
};
223+
224+
template<typename R1, typename R2>
225+
struct ratio_greater_equal: etl::integral_constant<bool, (R1::num * R2::den >= R2::num * R1::den)>
226+
{
227+
};
228+
229+
#if ETL_USING_CPP14
230+
template<typename R1, typename R2>
231+
ETL_CONSTEXPR14 bool ratio_equal_v = ratio_equal<R1, R2>::value;
232+
233+
template<typename R1, typename R2>
234+
ETL_CONSTEXPR14 bool ratio_not_equal_v = ratio_not_equal<R1, R2>::value;
235+
236+
template<typename R1, typename R2>
237+
ETL_CONSTEXPR14 bool ratio_less_v = ratio_less<R1, R2>::value;
238+
239+
template<typename R1, typename R2>
240+
ETL_CONSTEXPR14 bool ratio_less_equal_v = ratio_less_equal<R1, R2>::value;
241+
242+
template<typename R1, typename R2>
243+
ETL_CONSTEXPR14 bool ratio_greater_v = ratio_greater<R1, R2>::value;
244+
245+
template<typename R1, typename R2>
246+
ETL_CONSTEXPR14 bool ratio_greater_equal_v = ratio_greater_equal<R1, R2>::value;
247+
#endif
248+
#endif
100249
}
101250

102251
#endif

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,7 @@ add_executable(etl_tests
222222
test_queue_spsc_locked.cpp
223223
test_queue_spsc_locked_small.cpp
224224
test_random.cpp
225+
test_ratio.cpp
225226
test_reference_flat_map.cpp
226227
test_reference_flat_multimap.cpp
227228
test_reference_flat_multiset.cpp

test/test_ratio.cpp

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/******************************************************************************
2+
The MIT License(MIT)
3+
4+
Embedded Template Library.
5+
https://github.com/ETLCPP/etl
6+
https://www.etlcpp.com
7+
8+
Copyright(c) 2025 BMW AG
9+
10+
Permission is hereby granted, free of charge, to any person obtaining a copy
11+
of this software and associated documentation files(the "Software"), to deal
12+
in the Software without restriction, including without limitation the rights
13+
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
14+
copies of the Software, and to permit persons to whom the Software is
15+
furnished to do so, subject to the following conditions :
16+
17+
The above copyright notice and this permission notice shall be included in all
18+
copies or substantial portions of the Software.
19+
20+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
23+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26+
SOFTWARE.
27+
******************************************************************************/
28+
29+
#include "unit_test_framework.h"
30+
31+
#include "etl/ratio.h"
32+
33+
#if ETL_USING_CPP11
34+
namespace
35+
{
36+
SUITE(test_ratio)
37+
{
38+
//*************************************************************************
39+
TEST(test_ratio_add)
40+
{
41+
using r1 = etl::ratio<1, 2>;
42+
using r2 = etl::ratio<2, 3>;
43+
using r3 = etl::ratio_add<r1, r2>;
44+
45+
CHECK((etl::ratio_equal<r3, etl::ratio<7, 6>>::value));
46+
CHECK((!etl::ratio_equal<r3, etl::ratio<1, 6>>::value));
47+
}
48+
49+
//*************************************************************************
50+
TEST(test_ratio_subtract)
51+
{
52+
using r1 = etl::ratio<1, 2>;
53+
using r2 = etl::ratio<2, 3>;
54+
using r3 = etl::ratio_subtract<r1, r2>;
55+
56+
CHECK((etl::ratio_equal<r3, etl::ratio<-1, 6>>::value));
57+
CHECK((!etl::ratio_equal<r3, etl::ratio<-2, 6>>::value));
58+
}
59+
60+
//*************************************************************************
61+
TEST(test_ratio_multiply)
62+
{
63+
using r1 = etl::ratio<1, 2>;
64+
using r2 = etl::ratio<2, 3>;
65+
using r3 = etl::ratio_multiply<r1, r2>;
66+
67+
CHECK((etl::ratio_equal<r3, etl::ratio<1, 3>>::value));
68+
CHECK((!etl::ratio_equal<r3, etl::ratio<1, 4>>::value));
69+
}
70+
71+
//*************************************************************************
72+
TEST(test_ratio_divide)
73+
{
74+
using r1 = etl::ratio<1, 2>;
75+
using r2 = etl::ratio<2, 3>;
76+
using r3 = etl::ratio_divide<r1, r2>;
77+
78+
CHECK((!etl::ratio_not_equal<r3, etl::ratio<3, 4>>::value));
79+
CHECK((etl::ratio_not_equal<r3, etl::ratio<4, 3>>::value));
80+
}
81+
82+
//*************************************************************************
83+
TEST(test_ratio_equal)
84+
{
85+
using r1 = etl::ratio<1, 2>;
86+
using r2 = etl::ratio<2, 3>;
87+
88+
CHECK((etl::ratio_equal<r1, r1>::value));
89+
CHECK((!etl::ratio_equal<r1, r2>::value));
90+
}
91+
92+
//*************************************************************************
93+
TEST(test_ratio_not_equal)
94+
{
95+
using r1 = etl::ratio<1, 2>;
96+
using r2 = etl::ratio<2, 3>;
97+
98+
CHECK((!etl::ratio_not_equal<r1, r1>::value));
99+
CHECK((etl::ratio_not_equal<r1, r2>::value));
100+
}
101+
102+
//*************************************************************************
103+
TEST(test_ratio_less)
104+
{
105+
using r1 = etl::ratio<1, 2>;
106+
using r2 = etl::ratio<2, 3>;
107+
108+
CHECK((etl::ratio_less<r1, r2>::value));
109+
CHECK((!etl::ratio_less<r2, r1>::value));
110+
}
111+
112+
//*************************************************************************
113+
TEST(test_ratio_less_equal)
114+
{
115+
using r1 = etl::ratio<1, 2>;
116+
using r2 = etl::ratio<2, 3>;
117+
118+
CHECK((etl::ratio_less_equal<r1, r1>::value));
119+
CHECK((etl::ratio_less_equal<r1, r2>::value));
120+
}
121+
122+
//*************************************************************************
123+
TEST(test_ratio_greater)
124+
{
125+
using r1 = etl::ratio<4, 3>;
126+
using r2 = etl::ratio<2, 3>;
127+
128+
CHECK((etl::ratio_greater<r1, r2>::value));
129+
CHECK((!etl::ratio_greater<r2, r1>::value));
130+
}
131+
132+
//*************************************************************************
133+
TEST(test_ratio_greater_equal)
134+
{
135+
using r1 = etl::ratio<4, 3>;
136+
using r2 = etl::ratio<2, 3>;
137+
138+
CHECK((etl::ratio_greater_equal<r1, r1>::value));
139+
CHECK((etl::ratio_greater_equal<r1, r2>::value));
140+
}
141+
};
142+
143+
}
144+
#endif

0 commit comments

Comments
 (0)