Arene Base
Fundamental Utilities For Safety Critical C++
Loading...
Searching...
No Matches
ratio.hpp
Go to the documentation of this file.
1// Copyright 2026, Toyota Motor Corporation
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5#ifndef INCLUDE_GUARD_ARENE_BASE_STDLIB_INCLUDE_STDLIB_DETAIL_RATIO_HPP_
6#define INCLUDE_GUARD_ARENE_BASE_STDLIB_INCLUDE_STDLIB_DETAIL_RATIO_HPP_
7
8// IWYU pragma: private, include <ratio>
9// IWYU pragma: friend "stdlib_detail/.*"
10
11// parasoft-begin-suppress CERT_CPP-DCL58-a-2 "Part of a standard library implementation"
12// parasoft-begin-suppress AUTOSAR-A17_6_1-a-2 "Part of a standard library implementation"
13
14#include "arene/base/math/abs.hpp"
15#include "arene/base/math/gcd.hpp"
16#include "arene/base/utility/swap.hpp"
17#include "stdlib/include/stdlib_detail/cstdint.hpp"
18#include "stdlib/include/stdlib_detail/numeric_limits.hpp"
19
20namespace std {
21
22/// @brief A compile-time ratio of two integers, automatically reduced to lowest terms
23/// @tparam N The unreduced numerator of the ratio
24/// @tparam D The unreduced denominator of the ratio
25/// @pre The denominator must not be 0, and both numerator and denominator must have representable absolute values
26template <intmax_t N, intmax_t D = 1>
27class ratio {
28 static_assert(D != 0, "Denominator of a ratio must not be 0");
29 // parasoft-begin-suppress CERT_C-PRE31-c "False positive: neither static_assert nor min are macros"
30 static_assert(N != numeric_limits<intmax_t>::min(), "Numerator of ratio must have a representable absolute value");
31 static_assert(D != numeric_limits<intmax_t>::min(), "Denominator of ratio must have a representable absolute value");
32 // parasoft-end-suppress CERT_C-PRE31-c
33 /// @brief The greatest common divisor of the numerator and denominator, used to reduce them to lowest terms
34 static constexpr intmax_t gcd{::arene::base::gcd(N, D)};
35 /// @brief The sign of the denominator parameter (used to adjust the sign of the reduced numerator)
36 // parasoft-begin-suppress AUTOSAR-A5_16_1-a "False positive: this is not a sub-expression"
37 static constexpr intmax_t d_sign{D > 0 ? 1 : -1};
38 // parasoft-end-suppress AUTOSAR-A5_16_1-a
39
40 public:
41 // parasoft-begin-suppress AUTOSAR-M11_0_1-a "This is part of the C++14 standard API"
42 /// @brief The numerator of the ratio, reduced to lowest terms
43 static constexpr intmax_t num{d_sign * N / gcd};
44 /// @brief The denominator of the ratio, reduced to lowest terms
45 static constexpr intmax_t den{::arene::base::abs(D) / gcd};
46 // parasoft-end-suppress AUTOSAR-M11_0_1-a
47 /// @brief The type of an equivalent ratio whose *reduced* numerator and denominator are the same as this ratio
48 using type = ratio<num, den>;
49};
50
51namespace ratio_detail {
52/// @brief A type trait to check if a type is a specialization of @c std::ratio as required in [ratio.general]
53/// @tparam T The type to check; @c std::false_type if this is not a @c std::ratio specialization
54template <typename T>
55class is_ratio : public std::false_type {};
56
57/// @brief A type trait to check if a type is a specialization of @c std::ratio as required in [ratio.general]
58/// @tparam N The numerator of a ratio being passed through the check
59/// @tparam D The denominator of a ratio being passed through the check
60template <intmax_t N, intmax_t D>
61class is_ratio<ratio<N, D>> : public std::true_type {};
62
63/// @brief A helper for multiplication to enforce the type requirement in [ratio.general] and improve readability
64/// @tparam R1 The first of two ratios to be multiplied
65/// @tparam R2 The second of two ratios to be multiplied
66template <class R1, class R2>
68 static_assert(is_ratio<R1>::value, "Parameters to ratio arithmetic operations must be specializations of std::ratio");
69 static_assert(is_ratio<R2>::value, "Parameters to ratio arithmetic operations must be specializations of std::ratio");
70 // parasoft-begin-suppress AUTOSAR-M0_1_3-c "False positive: these variables are used"
71 /// @brief The GCD of the numerator of R1 with the denominator of R2, used to pre-emptively reduce both
72 static constexpr intmax_t gcd_12{::arene::base::gcd(R1::num, R2::den)};
73 /// @brief The GCD of the numerator of R2 with the denominator of R1, used to pre-emptively reduce both
74 static constexpr intmax_t gcd_21{::arene::base::gcd(R2::num, R1::den)};
75 // parasoft-end-suppress AUTOSAR-M0_1_3-c
76
77 public:
78 /// @brief The result of the multiplication
79 using type = ratio<(R1::num / gcd_12) * (R2::num / gcd_21), (R1::den / gcd_21) * (R2::den / gcd_12)>;
80};
81
82/// @brief A helper for addition to enforce the type requirement in [ratio.general] and improve readability
83/// @tparam R1 The first of two ratios to be added
84/// @tparam R2 The second of two ratios to be added
85template <class R1, class R2>
87 static_assert(is_ratio<R1>::value, "Parameters to ratio arithmetic operations must be specializations of std::ratio");
88 static_assert(is_ratio<R2>::value, "Parameters to ratio arithmetic operations must be specializations of std::ratio");
89 // parasoft-begin-suppress AUTOSAR-M0_1_3-c "False positive: these variables are used"
90 /// @brief The GCD of the numerators of the two ratios, used to pre-emptively reduce both
91 static constexpr intmax_t gcd_num{(R1::num == 0 && R2::num == 0) ? 1 : ::arene::base::gcd(R1::num, R2::num)};
92 /// @brief The GCD of the denominators of the two ratios, used to pre-emptively reduce both
93 static constexpr intmax_t gcd_den{::arene::base::gcd(R2::den, R1::den)};
94 // parasoft-end-suppress AUTOSAR-M0_1_3-c
95
96 /// @brief A few terms pulled out of the main addition to reduce the magnitudes of the operands as much as possible
97 using left = ratio<gcd_num, R2::den>;
98 /// @brief The main part of the addition, with each of the operands reduced by their GCD with something else
99 using right =
100 ratio<(R1::num / gcd_num) * (R2::den / gcd_den) + (R2::num / gcd_num) * (R1::den / gcd_den), R1::den / gcd_den>;
101
102 public:
103 /// @brief The result of the addition
105};
106
107/// @brief A helper for equality comparison to enforce the type requirement in [ratio.general]
108/// @tparam R1 The first of two ratios to be compared
109/// @tparam R2 The second of two ratios to be compared
110template <class R1, class R2>
112 static_assert(is_ratio<R1>::value, "Parameters to ratio comparison operations must be specializations of std::ratio");
113 static_assert(is_ratio<R2>::value, "Parameters to ratio comparison operations must be specializations of std::ratio");
114
115 public:
116 /// @brief The result of the comparison
117 using type = integral_constant<bool, R1::num == R2::num && R1::den == R2::den>;
118};
119
120/// @brief A type used to represent a fraction for non-template computations
121struct fraction {
122 /// @brief The numerator of the fraction
124 /// @brief The denominator of the fraction
126};
127
128// parasoft-begin-suppress AUTOSAR-A7_1_5-a-2 "Trailing return syntax permitted by A7-1-5 Permit #1 v1.0.0"
129
130/// @brief Check if two fractions can be directly compared (by cross multiplying them) without overflowing
131/// @param left The left side of the comparison
132/// @param right The right side of the comparison
133/// @return @c true if <tt>left < right</tt> can be directly computed without overflowing; @c false if not
134constexpr auto can_directly_compare(fraction const& left, fraction const& right) noexcept -> bool {
135 return (left.num == 0) || (right.num == 0) ||
136 ((left.num <= numeric_limits<intmax_t>::max() / right.den) &&
137 (right.num <= numeric_limits<intmax_t>::max() / left.den));
138}
139
140/// @brief Check if the left fraction is less than the right or not
141/// @param left The fraction on the left side of the comparison
142/// @param right The fraction on the right side of the comparison
143/// @return @c true if <tt>left < right</tt>, @c false if not
144constexpr auto fraction_less(fraction left, fraction right) noexcept -> bool {
145 bool flip_direction{false};
146
147 // Check shortcuts we can make based on the signs of the parameters
148 if (left.num < 0) {
149 if (right.num >= 0) {
150 return true; // left is negative, right isn't => definitely less
151 }
152 // Make both arguments positive and flip the comparison order
153 flip_direction = !flip_direction;
154 left.num = -left.num;
155 right.num = -right.num;
156 }
157 if (right.num < 0) {
158 return false; // right is negative, left isn't => definitely not less
159 }
160
161 // If one fraction is greater than 1 and the other isn't, the one that's greater than 1 is greater
162 if ((left.num > left.den) != (right.num > right.den)) {
163 return right.num > right.den;
164 }
165
166 // We'll repeatedly reduce the fractions until we can compare them successfully
167 while (!(can_directly_compare(left, right))) {
168 // If we can't multiply at all then we need to decompose the two into integral parts and fractional parts
169 flip_direction = !flip_direction;
170 ::arene::base::swap(left.num, left.den);
171 ::arene::base::swap(right.num, right.den);
172
173 // If the two have different integral parts, then we only need to compare the integral parts (which always works)
174 intmax_t const left_integ{left.num / left.den};
175 intmax_t const right_integ{right.num / right.den};
176 if (left_integ != right_integ) {
177 left = {left_integ, 1};
178 right = {right_integ, 1};
179 break;
180 }
181
182 // If the two have the same integral part, we subtract off the integral part and go again
183 left.num %= left.den;
184 right.num %= right.den;
185 }
186
187 return flip_direction ? right.num * left.den < right.den * left.num : left.num * right.den < left.den * right.num;
188}
189
190// parasoft-end-suppress AUTOSAR-A7_1_5-a-2
191
192/// @brief A helper for less-than comparison to enforce the type requirement in [ratio.general]
193/// @tparam R1 The first of two ratios to be compared
194/// @tparam R2 The second of two ratios to be compared
195template <class R1, class R2>
197 static_assert(is_ratio<R1>::value, "Parameters to ratio comparison operations must be specializations of std::ratio");
198 static_assert(is_ratio<R2>::value, "Parameters to ratio comparison operations must be specializations of std::ratio");
199
200 public:
201 /// @brief The result of the comparison
203};
204
205} // namespace ratio_detail
206
207/// @brief Defines the ratio yielded by multiplying the two given ratios
208/// @tparam R1 The first multiplicand
209/// @tparam R2 The second multiplicand
210///
211/// @note Performs reduction before multiplication so this should always work as long as the result is representable.
212template <class R1, class R2>
213using ratio_multiply = typename ratio_detail::ratio_multiply_impl<R1, R2>::type;
214
215// parasoft-begin-suppress AUTOSAR-A2_7_3-a "False positive: typedef *is* preceded by a comment with @brief"
216
217/// @brief Defines the ratio yielded by dividing the two given ratios
218/// @tparam R1 The dividend
219/// @tparam R2 The divisor
220///
221/// @note Performs reduction before division so this should always work as long as the result is representable.
222template <class R1, class R2>
224
225// parasoft-end-suppress AUTOSAR-A2_7_3-a
226
227/// @brief Defines the ratio yielded by adding the two given ratios
228/// @tparam R1 The first addend
229/// @tparam R2 The second addend
230///
231/// @note Performs reduction before addition so this should always work as long as the result is representable.
232template <class R1, class R2>
233using ratio_add = typename ratio_detail::ratio_add_impl<R1, R2>::type;
234
235/// @brief Defines the ratio yielded by subtracting the two given ratios
236/// @tparam R1 The minuend
237/// @tparam R2 The subtrahend
238///
239/// @note Performs reduction before subtraction so this should always work as long as the result is representable.
240template <class R1, class R2>
242
243/// @brief Inherits from @c true_type if the first ratio is equal to the second, @c false_type if not
244/// @tparam R1 The first ratio
245/// @tparam R2 The second ratio
246template <class R1, class R2>
247class ratio_equal : public ratio_detail::ratio_equal_impl<R1, R2>::type {};
248
249/// @brief Inherits from @c true_type if the first ratio is not equal to the second, @c false_type if they are equal
250/// @tparam R1 The first ratio
251/// @tparam R2 The second ratio
252template <class R1, class R2>
254
255/// @brief Inherits from @c true_type if the first ratio is less than the second, @c false_type if not
256/// @tparam R1 The first ratio
257/// @tparam R2 The second ratio
258template <class R1, class R2>
259class ratio_less : public ratio_detail::ratio_less_impl<R1, R2>::type {};
260
261/// @brief Inherits from @c true_type if the first ratio is less than or equal to the second, @c false_type if not
262/// @tparam R1 The first ratio
263/// @tparam R2 The second ratio
264template <class R1, class R2>
266
267/// @brief Inherits from @c true_type if the first ratio is greater than the second, @c false_type if not
268/// @tparam R1 The first ratio
269/// @tparam R2 The second ratio
270template <class R1, class R2>
272
273/// @brief Inherits from @c true_type if the first ratio is greater than or equal to the second, @c false_type if not
274/// @tparam R1 The first ratio
275/// @tparam R2 The second ratio
276template <class R1, class R2>
278
279// NOLINTBEGIN(readability-magic-numbers) These are named constants to replace users' magic numbers
280/// @brief The SI prefix for 10^(-18), expressed as a @c ratio
281using atto = ratio<1, 1'000'000'000'000'000'000>;
282/// @brief The SI prefix for 10^(-15), expressed as a @c ratio
283using femto = ratio<1, 1'000'000'000'000'000>;
284/// @brief The SI prefix for 10^(-12), expressed as a @c ratio
285using pico = ratio<1, 1'000'000'000'000>;
286/// @brief The SI prefix for 10^(-9), expressed as a @c ratio
287using nano = ratio<1, 1'000'000'000>;
288/// @brief The SI prefix for 10^(-6), expressed as a @c ratio
289using micro = ratio<1, 1'000'000>;
290/// @brief The SI prefix for 10^(-3), expressed as a @c ratio
291using milli = ratio<1, 1'000>;
292/// @brief The SI prefix for 10^(-2), expressed as a @c ratio
293using centi = ratio<1, 100>;
294/// @brief The SI prefix for 10^(-1), expressed as a @c ratio
295using deci = ratio<1, 10>;
296/// @brief The SI prefix for 10^1, expressed as a @c ratio
297using deca = ratio<10, 1>;
298/// @brief The SI prefix for 10^2, expressed as a @c ratio
299using hecto = ratio<100, 1>;
300/// @brief The SI prefix for 10^3, expressed as a @c ratio
301using kilo = ratio<1'000, 1>;
302/// @brief The SI prefix for 10^6, expressed as a @c ratio
303using mega = ratio<1'000'000, 1>;
304/// @brief The SI prefix for 10^9, expressed as a @c ratio
305using giga = ratio<1'000'000'000, 1>;
306/// @brief The SI prefix for 10^12, expressed as a @c ratio
307using tera = ratio<1'000'000'000'000, 1>;
308/// @brief The SI prefix for 10^15, expressed as a @c ratio
309using peta = ratio<1'000'000'000'000'000, 1>;
310/// @brief The SI prefix for 10^18, expressed as a @c ratio
311using exa = ratio<1'000'000'000'000'000'000, 1>;
312// NOLINTEND(readability-magic-numbers)
313
314} // namespace std
315
316#endif // INCLUDE_GUARD_ARENE_BASE_STDLIB_INCLUDE_STDLIB_DETAIL_RATIO_HPP_
A type trait to check if a type is a specialization of std::ratio as required in [ratio....
Definition ratio.hpp:55
A helper for addition to enforce the type requirement in [ratio.general] and improve readability.
Definition ratio.hpp:86
A helper for equality comparison to enforce the type requirement in [ratio.general].
Definition ratio.hpp:111
A helper for less-than comparison to enforce the type requirement in [ratio.general].
Definition ratio.hpp:196
A helper for multiplication to enforce the type requirement in [ratio.general] and improve readabilit...
Definition ratio.hpp:67
Inherits from true_type if the first ratio is equal to the second, false_type if not.
Definition ratio.hpp:247
Inherits from true_type if the first ratio is greater than or equal to the second,...
Definition ratio.hpp:277
Inherits from true_type if the first ratio is greater than the second, false_type if not.
Definition ratio.hpp:271
Inherits from true_type if the first ratio is less than or equal to the second, false_type if not.
Definition ratio.hpp:265
Inherits from true_type if the first ratio is less than the second, false_type if not.
Definition ratio.hpp:259
Inherits from true_type if the first ratio is not equal to the second, false_type if they are equal.
Definition ratio.hpp:253
A compile-time ratio of two integers, automatically reduced to lowest terms.
Definition ratio.hpp:27
static constexpr intmax_t num
The numerator of the ratio, reduced to lowest terms.
Definition ratio.hpp:43
static constexpr intmax_t den
The denominator of the ratio, reduced to lowest terms.
Definition ratio.hpp:45
Definition ratio.hpp:51
constexpr auto can_directly_compare(fraction const &left, fraction const &right) noexcept -> bool
Check if two fractions can be directly compared (by cross multiplying them) without overflowing.
Definition ratio.hpp:134
constexpr auto fraction_less(fraction left, fraction right) noexcept -> bool
Check if the left fraction is less than the right or not.
Definition ratio.hpp:144
constexpr auto operator()(::arene::base::result< void, E > const &value) const noexcept(noexcept(hash< E >{}(std::declval< E const & >()))) -> std::size_t
Calculate the hash of a result.
Definition result.hpp:1827
A type used to represent a fraction for non-template computations.
Definition ratio.hpp:121
intmax_t num
The numerator of the fraction.
Definition ratio.hpp:123
intmax_t den
The denominator of the fraction.
Definition ratio.hpp:125