9
9
#include < type_traits>
10
10
11
11
#ifndef FPM_NODISCARD
12
- # if __cplusplus >= 201703L
12
+ # if __cplusplus >= 201703L /* C++17 */
13
13
# define FPM_NODISCARD [[nodiscard]]
14
14
# else
15
15
# define FPM_NODISCARD
@@ -33,12 +33,14 @@ class fixed
33
33
static_assert (sizeof (IntermediateType) > sizeof (BaseType), " IntermediateType must be larger than BaseType" );
34
34
static_assert (std::numeric_limits<IntermediateType>::is_signed == std::numeric_limits<BaseType>::is_signed, " IntermediateType must have same signedness as BaseType" );
35
35
36
+ public:
36
37
// / Although this value fits in the BaseType in terms of bits, if there's only one integral bit, this value
37
38
// / is incorrect (flips from positive to negative), so we must extend the size to IntermediateType.
38
39
static constexpr IntermediateType FRACTION_MULT = IntermediateType(1 ) << FractionBits;
39
40
40
41
#pragma region Constructors
41
42
43
+ private:
42
44
struct raw_construct_tag {};
43
45
constexpr inline fixed (BaseType val, raw_construct_tag) noexcept : m_value(val) {}
44
46
@@ -154,6 +156,8 @@ class fixed
154
156
return m_value != 0 ;
155
157
}
156
158
159
+ #pragma region Addition
160
+
157
161
inline fixed& operator +=(const fixed& y) noexcept
158
162
{
159
163
m_value += y.m_value ;
@@ -167,6 +171,10 @@ class fixed
167
171
return *this ;
168
172
}
169
173
174
+ #pragma endregion
175
+
176
+ #pragma region Subtraction
177
+
170
178
inline fixed& operator -=(const fixed& y) noexcept
171
179
{
172
180
m_value -= y.m_value ;
@@ -180,6 +188,10 @@ class fixed
180
188
return *this ;
181
189
}
182
190
191
+ #pragma endregion
192
+
193
+ #pragma region Multiplication
194
+
183
195
inline fixed& operator *=(const fixed& y) noexcept
184
196
{
185
197
if (EnableRounding){
@@ -202,6 +214,10 @@ class fixed
202
214
return *this ;
203
215
}
204
216
217
+ #pragma endregion
218
+
219
+ #pragma region Division
220
+
205
221
inline fixed& operator /=(const fixed& y) noexcept
206
222
{
207
223
assert (y.m_value != 0 );
@@ -227,6 +243,8 @@ class fixed
227
243
228
244
#pragma endregion
229
245
246
+ #pragma endregion
247
+
230
248
private:
231
249
BaseType m_value;
232
250
};
@@ -241,6 +259,8 @@ using fixed_8_24 = fixed<std::int32_t, std::int64_t, 24>;
241
259
242
260
#pragma endregion
243
261
262
+ #pragma region Arithmetic operators
263
+
244
264
#pragma region Addition
245
265
246
266
template <typename B, typename I, unsigned int F, bool R>
@@ -290,19 +310,28 @@ FPM_NODISCARD constexpr inline fixed<B, I, F, R> operator-(T x, const fixed<B, I
290
310
template <typename B, typename I, unsigned int F, bool R>
291
311
FPM_NODISCARD constexpr inline fixed<B, I, F, R> operator *(const fixed<B, I, F, R>& x, const fixed<B, I, F, R>& y) noexcept
292
312
{
293
- return fixed<B, I, F, R>(x) *= y;
313
+ if (R){
314
+ // Normal fixed-point multiplication is: x * y / 2**FractionBits.
315
+ // To correctly round the last bit in the result, we need one more bit of information.
316
+ // We do this by multiplying by two before dividing and adding the LSB to the real result.
317
+ const auto value = (static_cast <I>(x.raw_value ()) * y.raw_value ()) / (fixed<B, I, F, R>::FRACTION_MULT / 2 );
318
+ return fixed<B, I, F, R>::from_raw_value (static_cast <B>((value / 2 ) + (value % 2 )));
319
+ } else {
320
+ const auto value = (static_cast <I>(x.raw_value ()) * y.raw_value ()) / fixed<B, I, F, R>::FRACTION_MULT;
321
+ return fixed<B, I, F, R>::from_raw_value (static_cast <B>(value));
322
+ }
294
323
}
295
324
296
325
template <typename B, typename I, unsigned int F, bool R, typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr >
297
326
FPM_NODISCARD constexpr inline fixed<B, I, F, R> operator *(const fixed<B, I, F, R>& x, T y) noexcept
298
327
{
299
- return fixed<B, I, F, R>(x ) *= y ;
328
+ return fixed<B, I, F, R>:: from_raw_value (x. raw_value ( ) * y) ;
300
329
}
301
330
302
331
template <typename B, typename I, unsigned int F, bool R, typename T, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr >
303
332
FPM_NODISCARD constexpr inline fixed<B, I, F, R> operator *(T x, const fixed<B, I, F, R>& y) noexcept
304
333
{
305
- return fixed<B, I, F, R>(y) *= x ;
334
+ return fixed<B, I, F, R>:: from_raw_value (x * y. raw_value ()) ;
306
335
}
307
336
#pragma endregion
308
337
@@ -311,23 +340,35 @@ FPM_NODISCARD constexpr inline fixed<B, I, F, R> operator*(T x, const fixed<B, I
311
340
template <typename B, typename I, unsigned int F, bool R>
312
341
FPM_NODISCARD constexpr inline fixed<B, I, F, R> operator /(const fixed<B, I, F, R>& x, const fixed<B, I, F, R>& y) noexcept
313
342
{
314
- return fixed<B, I, F, R>(x) /= y;
343
+ assert (y.raw_value () != 0 );
344
+ if (R){
345
+ // Normal fixed-point division is: x * 2**FractionBits / y.
346
+ // To correctly round the last bit in the result, we need one more bit of information.
347
+ // We do this by multiplying by two before dividing and adding the LSB to the real result.
348
+ const auto value = (static_cast <I>(x.raw_value ()) * fixed<B, I, F, R>::FRACTION_MULT * 2 ) / y.raw_value ();
349
+ return fixed<B, I, F, R>::from_raw_value (static_cast <B>((value / 2 ) + (value % 2 )));
350
+ } else {
351
+ const auto value = (static_cast <I>(x.raw_value ()) * fixed<B, I, F, R>::FRACTION_MULT) / y.raw_value ();
352
+ return fixed<B, I, F, R>::from_raw_value (static_cast <B>(value));
353
+ }
315
354
}
316
355
317
356
template <typename B, typename I, unsigned int F, typename T, bool R, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr >
318
357
FPM_NODISCARD constexpr inline fixed<B, I, F, R> operator /(const fixed<B, I, F, R>& x, T y) noexcept
319
358
{
320
- return fixed<B, I, F, R>(x ) /= y ;
359
+ return fixed<B, I, F, R>:: from_raw_value (x. raw_value ( ) / y) ;
321
360
}
322
361
323
362
template <typename B, typename I, unsigned int F, typename T, bool R, typename std::enable_if<std::is_integral<T>::value>::type* = nullptr >
324
363
FPM_NODISCARD constexpr inline fixed<B, I, F, R> operator /(T x, const fixed<B, I, F, R>& y) noexcept
325
364
{
326
- return fixed<B, I, F, R>(x) /= y;
365
+ return fixed<B, I, F, R>(x) / y;
327
366
}
328
367
329
368
#pragma endregion
330
369
370
+ #pragma endregion
371
+
331
372
#pragma region Comparison operators
332
373
333
374
template <typename B, typename I, unsigned int F, bool R>
@@ -348,7 +389,7 @@ FPM_NODISCARD constexpr inline bool operator==(const T x, const fixed<B, I, F, R
348
389
return fixed<B, I, F, R>{x} == y;
349
390
}
350
391
351
- #if __cplusplus >= 202002L
392
+ #if __cplusplus >= 202002L /* C++20 */
352
393
353
394
template <typename B, typename I, unsigned int F, bool R>
354
395
FPM_NODISCARD constexpr inline auto operator <=>(const fixed<B, I, F, R>& x, const fixed<B, I, F, R>& y) noexcept
@@ -550,7 +591,7 @@ struct is_fixed : std::false_type {};
550
591
template <typename BaseType, typename IntermediateType, unsigned int FractionBits, bool EnableRounding>
551
592
struct is_fixed <fixed<BaseType, IntermediateType, FractionBits, EnableRounding>> : std::true_type {};
552
593
553
- #if __cplusplus >= 201703L
594
+ #if __cplusplus >= 201703L /* C++17 */
554
595
template <typename T>
555
596
inline constexpr bool is_fixed_v = is_fixed<T>::value;
556
597
#endif
0 commit comments