Monado OpenXR Runtime
m_rational.hpp
Go to the documentation of this file.
1// Copyright 2022, Collabora, Ltd.
2// SPDX-License-Identifier: BSL-1.0
3/*!
4 * @file
5 * @brief A very simple rational number type.
6 * @author Rylie Pavlik <rylie.pavlik@collabora.com>
7 * @ingroup aux_math
8 */
9
10#pragma once
11
12#ifndef __cplusplus
13#error "This header is C++-only."
14#endif
15
16#include <type_traits>
17#include <limits>
18#include <stdexcept>
19
20namespace xrt::auxiliary::math {
21
22/*!
23 * A rational (fractional) number type.
24 */
25template <typename Scalar> struct Rational
26{
27 static_assert(std::is_integral_v<Scalar>, "This type is only for use with integer components.");
28
29 using value_type = Scalar;
30 value_type numerator;
31 value_type denominator;
32
33 /*!
34 * Return the rational value 1/1, the simplest unity (== 1) value.
35 */
36 static constexpr Rational<Scalar>
37 simplestUnity() noexcept
38 {
39 return {1, 1};
40 }
41
42 /*!
43 * Return the reciprocal of this value.
44 *
45 * Result will have a non-negative denominator.
46 */
47 constexpr Rational
48 reciprocal() const noexcept
49 {
50 return Rational{denominator, numerator}.withNonNegativeDenominator();
51 }
52
53 /*!
54 * Return this value, with the denominator non-negative (0 or positive).
55 */
56 constexpr Rational
58 {
59 if constexpr (std::is_unsigned_v<Scalar>) {
60 // unsigned means always non-negative
61 return *this;
62 } else {
63 return denominator < Scalar{0} ? Rational{-numerator, -denominator} : *this;
64 }
65 }
66
67 /*!
68 * Does this rational number represent a value greater than 1, with a positive denominator?
69 *
70 */
71 constexpr bool
72 isOverUnity() const noexcept
73 {
74 return numerator > denominator && denominator > Scalar{0};
75 }
76
77 /*!
78 * Does this rational number represent 1?
79 *
80 * @note false if denominator is 0, even if numerator is also 0.
81 */
82 constexpr bool
83 isUnity() const noexcept
84 {
85 return numerator == denominator && denominator != Scalar{0};
86 }
87
88 /*!
89 * Does this rational number represent 0?
90 *
91 * @note false if denominator is 0, even if numerator is also 0.
92 */
93 constexpr bool
94 isZero() const noexcept
95 {
96 return numerator == Scalar{0} && denominator != Scalar{0};
97 }
98
99 /*!
100 * Does this rational number represent a value between 0 and 1 (exclusive), and has a positive denominator?
101 *
102 * This is the most common useful range.
103 */
104 constexpr bool
105 isBetweenZeroAndOne() const noexcept
106 {
107 return denominator > Scalar{0} && numerator > Scalar{0} && numerator < denominator;
108 }
109
110 /*!
111 * Get the complementary fraction.
112 *
113 * Only really makes sense if isBetweenZeroAndOne() is true
114 *
115 * Result will have a non-negative denominator.
116 */
117 constexpr Rational
118 complement() const noexcept
119 {
120 return Rational{denominator - numerator, denominator}.withNonNegativeDenominator();
121 }
122
123 /*!
124 * Get an approximation of this value as a float.
125 */
126 constexpr float
127 as_float() const noexcept
128 {
129 return static_cast<float>(numerator) / static_cast<float>(denominator);
130 }
131
132 /*!
133 * Get an approximation of this value as a double.
134 */
135 constexpr double
136 as_double() const noexcept
137 {
138 return static_cast<double>(numerator) / static_cast<double>(denominator);
139 }
140};
141
142/*!
143 * Multiplication operator. Warning: does no simplification!
144 *
145 * Result will have a non-negative denominator.
146 *
147 * @relates Rational
148 */
149template <typename Scalar>
150constexpr Rational<Scalar>
152{
153 return Rational<Scalar>{lhs.numerator * rhs.numerator, lhs.denominator * rhs.denominator}
154 .withNonNegativeDenominator();
155}
156
157/*!
158 * Multiplication operator with a scalar. Warning: does no simplification!
159 *
160 * Result will have a non-negative denominator.
161 *
162 * @relates Rational
163 */
164template <typename Scalar>
165constexpr Rational<Scalar>
166operator*(const Rational<Scalar> &lhs, const Scalar &rhs)
167{
168 return Rational<Scalar>{lhs.numerator * rhs, lhs.denominator}.withNonNegativeDenominator();
169}
170
171/*!
172 * Multiplication operator with a scalar. Warning: does no simplification!
173 *
174 * Result will have a non-negative denominator.
175 *
176 * @relates Rational
177 */
178template <typename Scalar>
179constexpr Rational<Scalar>
180operator*(const Scalar &lhs, const Rational<Scalar> &rhs)
181{
182 return (rhs * lhs).withNonNegativeDenominator();
183}
184
185/*!
186 * Equality comparison operator. Warning: does no simplification, looks for exact equality!
187 *
188 * @relates Rational
189 */
190template <typename Scalar>
191constexpr bool
193{
194 return rhs.numerator == lhs.numerator && rhs.denominator == lhs.denominator;
195}
196
197/*!
198 * Division operator. Warning: does no simplification!
199 *
200 * Result will have a non-negative denominator.
201 *
202 * @relates Rational
203 */
204template <typename Scalar>
205constexpr Rational<Scalar>
207{
208 return (lhs * rhs.reciprocal()).withNonNegativeDenominator();
209}
210
211
212/*!
213 * Division operator by a scalar. Warning: does no simplification!
214 *
215 * Result will have a non-negative denominator.
216 *
217 * @relates Rational
218 */
219template <typename Scalar>
220constexpr Rational<Scalar>
222{
223 return (lhs * Rational<Scalar>{1, rhs});
224}
225
226
227} // namespace xrt::auxiliary::math
C++-only functionality in the Math helper library.
Definition: m_documentation.hpp:15
A rational (fractional) number type.
Definition: m_rational.hpp:26
constexpr Rational< Scalar > operator*(const Scalar &lhs, const Rational< Scalar > &rhs)
Multiplication operator with a scalar.
Definition: m_rational.hpp:180
constexpr Rational reciprocal() const noexcept
Return the reciprocal of this value.
Definition: m_rational.hpp:48
constexpr float as_float() const noexcept
Get an approximation of this value as a float.
Definition: m_rational.hpp:127
constexpr bool isUnity() const noexcept
Does this rational number represent 1?
Definition: m_rational.hpp:83
constexpr Rational< Scalar > operator/(const Rational< Scalar > &lhs, const Rational< Scalar > &rhs)
Division operator.
Definition: m_rational.hpp:206
constexpr double as_double() const noexcept
Get an approximation of this value as a double.
Definition: m_rational.hpp:136
constexpr Rational< Scalar > operator*(const Rational< Scalar > &lhs, const Rational< Scalar > &rhs)
Multiplication operator.
Definition: m_rational.hpp:151
constexpr bool operator==(const Rational< Scalar > &lhs, const Rational< Scalar > &rhs)
Equality comparison operator.
Definition: m_rational.hpp:192
constexpr bool isBetweenZeroAndOne() const noexcept
Does this rational number represent a value between 0 and 1 (exclusive), and has a positive denominat...
Definition: m_rational.hpp:105
constexpr bool isZero() const noexcept
Does this rational number represent 0?
Definition: m_rational.hpp:94
constexpr Rational withNonNegativeDenominator() const noexcept
Return this value, with the denominator non-negative (0 or positive).
Definition: m_rational.hpp:57
static constexpr Rational< Scalar > simplestUnity() noexcept
Return the rational value 1/1, the simplest unity (== 1) value.
Definition: m_rational.hpp:37
constexpr Rational< Scalar > operator*(const Rational< Scalar > &lhs, const Scalar &rhs)
Multiplication operator with a scalar.
Definition: m_rational.hpp:166
constexpr Rational complement() const noexcept
Get the complementary fraction.
Definition: m_rational.hpp:118
constexpr bool isOverUnity() const noexcept
Does this rational number represent a value greater than 1, with a positive denominator?
Definition: m_rational.hpp:72
constexpr Rational< Scalar > operator/(const Rational< Scalar > &lhs, Scalar rhs)
Division operator by a scalar.
Definition: m_rational.hpp:221