libfxd 0.2.dev
A fixed-point library for C++.
Loading...
Searching...
No Matches
compare.hpp
Go to the documentation of this file.
1/*
2 * libfxd - a fixed-point library for C++
3 *
4 * Copyright 2023 Daniel K. O.
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8#ifndef LIBFXD_COMPARE_HPP
9#define LIBFXD_COMPARE_HPP
10
11#include <compare>
12#include <utility>
13
14#include "detail/shift.hpp"
15
16
17namespace fxd {
18
19
21 template<fixed_point Fxd>
22 [[nodiscard]]
23 constexpr
24 bool
26 Fxd b)
27 noexcept
28 {
29 return a.raw_value == b.raw_value;
30 }
31
32
34 template<fixed_point A,
35 fixed_point B>
36 [[nodiscard]]
37 constexpr
38 bool
40 B b)
41 noexcept
42 {
43 bool different_signs = (a.raw_value < 0) != (b.raw_value < 0);
44 if (different_signs)
45 return false;
46
47 auto raw_a = a.raw_value;
48 auto raw_b = b.raw_value;
49
50 /*
51 * Algorithm: shift down the one with the most fractional bits,
52 * to align the point.
53 * If any 1 bit is lost, they're different.
54 * Otherwise, just compare the lined-up values as regular integers.
55 */
56 constexpr int diff = A::frac_bits - B::frac_bits;
57 if constexpr (diff > 0) {
58 // shift down A
59 auto [scaled_a, ovf] = detail::overflow::shr_real(raw_a, diff);
60 if (ovf)
61 return false;
62 return std::cmp_equal(scaled_a, raw_b);
63 } else {
64 // shift down B
65 auto [scaled_b, ovf] = detail::overflow::shr_real(raw_b, -diff);
66 if (ovf)
67 return false;
68 return std::cmp_equal(raw_a, scaled_b);
69 }
70 }
71
72
74 template<fixed_point Fxd,
75 std::integral I>
76 [[nodiscard]]
77 constexpr
78 bool
80 I b)
81 noexcept
82 {
83 if constexpr (Fxd::frac_bits < 0) {
84 auto [hi_b, ovf] = detail::overflow::shr_real(b, -Fxd::frac_bits);
85 if (ovf)
86 return false;
87 return std::cmp_equal(a.raw_value, hi_b);
88 } else {
89 auto [hi_a, ovf] = detail::overflow::shr_real(a.raw_value, Fxd::frac_bits);
90 if (ovf)
91 return false;
92 return std::cmp_equal(hi_a, b);
93 }
94 }
95
96
98 template<fixed_point Fxd,
99 std::floating_point Flt>
100 requires (requires (Fxd fxd) {to_float(fxd);})
101 [[nodiscard]]
102 constexpr
103 bool
104 operator ==(Fxd a,
105 Flt b)
106 noexcept
107 {
108 return to_float(a) == b;
109 }
110
111
112
113 template<fixed_point Fxd>
114 [[nodiscard]]
115 constexpr
116 std::strong_ordering
117 operator <=>(Fxd a,
118 Fxd b)
119 noexcept
120 {
121 // workaround for GCC bug: it doesn't like mixing spaceship operator with bitfields
122 using Raw = typename Fxd::raw_type;
123 Raw ra = a.raw_value;
124 Raw rb = b.raw_value;
125 return ra <=> rb;
126 }
127
128
129 template<fixed_point A,
130 fixed_point B>
131 [[nodiscard]]
132 constexpr
133 std::strong_ordering
134 operator <=>(A a,
135 B b)
136 noexcept
137 {
138 bool a_neg = a.raw_value < 0;
139 bool b_neg = b.raw_value < 0;
140 if (a_neg && !b_neg)
141 return std::strong_ordering::less;
142 if (!a_neg && b_neg)
143 return std::strong_ordering::greater;
144 // after this, they have same sign
145
146 /*
147 * Algorithm: line up the point by shifting down the one with more fractional bits.
148 *
149 * If the high bits are enough to define the order, we don't need the
150 * fractional bits.
151 *
152 * Otherwise, the bits shifted out decide if they're equal or different; if any
153 * bit was shifted out, that value is larger. It works for both positive and
154 * negative values.
155 */
156
157 constexpr int diff = A::frac_bits - B::frac_bits;
158 const auto raw_a = a.raw_value;
159 const auto raw_b = b.raw_value;
160
161 if (raw_a == 0 && raw_b == 0)
162 return std::strong_ordering::equal;
163
164 if constexpr (diff > 0) {
165 // shift down A
166 auto [scaled_a, ovf] = detail::overflow::shr_real(raw_a, diff);
167 if (std::cmp_not_equal(scaled_a, raw_b))
168 return scaled_a <=> raw_b;
169 if (ovf)
170 return std::strong_ordering::greater;
171 return std::strong_ordering::equal;
172 } else {
173 // shift down B
174 auto [scaled_b, ovf] = detail::overflow::shr_real(raw_b, -diff);
175 if (std::cmp_not_equal(raw_a, scaled_b))
176 return raw_a <=> scaled_b;
177 if (ovf)
178 return std::strong_ordering::less;
179 return std::strong_ordering::equal;
180 }
181 }
182
183
184 template<fixed_point Fxd,
185 std::integral I>
186 [[nodiscard]]
187 constexpr
188 std::strong_ordering
189 operator <=>(Fxd a,
190 I b)
191 noexcept
192 {
193 if constexpr (Fxd::frac_bits < 0) {
194 auto [hi_b, ovf] = detail::overflow::shr_real(b, -Fxd::frac_bits);
195 auto cmp = a.raw_value <=> hi_b;
196 if (cmp != 0 || !ovf) // top bits are different, or bottom bits are all zeros
197 return cmp;
198 // top bits are equal (a and b have same sign), and b has non-zero bottom bits
199 return std::strong_ordering::less;
200 } else {
201 auto [int_a, ovf] = detail::overflow::shr_real(a.raw_value, Fxd::frac_bits);
202 auto cmp = int_a <=> b;
203 if (cmp != 0 || !ovf)
204 return cmp;
205 return std::strong_ordering::greater;
206 }
207 }
208
209
210 template<fixed_point Fxd,
211 std::floating_point Flt>
212 requires (requires (Fxd a) {to_float(a);})
213 [[nodiscard]]
214 constexpr
215 std::partial_ordering
216 operator <=>(Fxd a,
217 Flt b)
218 noexcept
219 {
220 return to_float(a) <=> b;
221 }
222
223
224
225}
226
227
228#endif
This is the namespace where the entire library is defined.
Definition: casting.hpp:19
constexpr bool operator==(Fxd a, Fxd b) noexcept
Equality check for two similar fxd::fixed.
Definition: compare.hpp:25
constexpr Flt to_float(Fxd f) noexcept
Convert a fixed-point to a floating-point type, rounds to zero.
Definition: conversions.hpp:98
constexpr std::strong_ordering operator<=>(Fxd a, Fxd b) noexcept
Definition: compare.hpp:117