OXYGENHUB
◆ main openmsx / src / utils / FixedPoint.hh
6.5 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#ifndef FIXEDPOINT_HH
2
#define FIXEDPOINT_HH
3
4
#include "narrow.hh"
5
#include "serialize.hh"
6
#include <cmath>
7
#include <cstdint>
8
9
namespace openmsx {
10
11
/** A fixed point number, implemented by a 32-bit signed integer.
12
  * The FRACTION_BITS template argument selects the position of the "binary
13
  * point" (base 2 equivalent to decimal point).
14
  */
15
template<unsigned FRACTION_BITS_>
16
class FixedPoint {
17
public:
18
	/** Number of fractional bits (export template parameter as a constant
19
	  * so that external code can use it more easily). */
20
	static constexpr unsigned FRACTION_BITS = FRACTION_BITS_;
21
22
private:
23
	/** Fixed point representation of 1.
24
	  */
25
	static constexpr int ONE = 1 << FRACTION_BITS;
26
27
	/** Precalculated float value of 1 / ONE.
28
	  */
29
	static constexpr float INV_ONE_F = 1.0f / ONE;
30
31
	/** Precalculated double value of 1 / ONE.
32
	  */
33
	static constexpr double INV_ONE_D = 1.0 / ONE;
34
35
	/** Bitmask to filter out the fractional part of a fixed point
36
	  * representation.
37
	  */
38
	static constexpr int FRACTION_MASK = ONE - 1;
39
40
public:
41
	/** Create new fixed point object from given representation.
42
	  * Used by the overloaded operators.
43
	  * @param value the internal representation.
44
	  */
45
	[[nodiscard]] static constexpr FixedPoint create(int value) {
46
		return FixedPoint(value, CreateRawTag{});
47
	}
48
49
	/** Creates a zero-initialized fixed point object.
50
	  */
51
	constexpr FixedPoint() = default;
52
53
	// Conversion to fixed point:
54
55
	explicit constexpr FixedPoint(int i)      : value(i << FRACTION_BITS) {}
56
	explicit constexpr FixedPoint(unsigned i) : value(i << FRACTION_BITS) {}
57
	explicit FixedPoint(float  f) : value(narrow_cast<int>(lrintf(f * ONE))) {}
58
	explicit FixedPoint(double d) : value(narrow_cast<int>(lrint (d * ONE))) {}
59
60
	[[nodiscard]] static constexpr FixedPoint roundRatioDown(unsigned n, unsigned d) {
61
		return create(narrow_cast<int>((static_cast<uint64_t>(n) << FRACTION_BITS) / d));
62
	}
63
64
	[[nodiscard]] static constexpr int shiftHelper(int x, int s) {
65
		return (s >= 0) ? (x >> s) : (x << -s);
66
	}
67
	template<unsigned BITS2>
68
	explicit constexpr FixedPoint(FixedPoint<BITS2> other)
69
		: value(shiftHelper(other.getRawValue(), BITS2 - FRACTION_BITS)) {}
70
71
	// Conversion from fixed point:
72
73
	/**
74
	 * Returns the integer part (rounded down) of this fixed point number.
75
	 * Note that for negative numbers, rounding occurs away from zero.
76
	 */
77
	[[nodiscard]] constexpr int toInt() const {
78
		return value >> FRACTION_BITS;
79
	}
80
81
	/**
82
	 * Returns the float value that corresponds to this fixed point number.
83
	 */
84
	[[nodiscard]] constexpr float toFloat() const {
85
		return narrow_cast<float>(value) * INV_ONE_F;
86
	}
87
88
	/**
89
	 * Returns the double value that corresponds to this fixed point number.
90
	 */
91
	[[nodiscard]] constexpr double toDouble() const {
92
		return narrow_cast<double>(value) * INV_ONE_D;
93
	}
94
95
	/**
96
	 * Returns the fractional part of this fixed point number as a float.
97
	 * The fractional part is never negative, even for negative fixed point
98
	 * numbers.
99
	 * x.toInt() + x.fractionAsFloat() is approximately equal to x.toFloat()
100
	 */
101
	[[nodiscard]] constexpr float fractionAsFloat() const {
102
		return narrow_cast<float>(value & FRACTION_MASK) * INV_ONE_F;
103
	}
104
105
	/**
106
	 * Returns the fractional part of this fixed point number as a double.
107
	 * The fractional part is never negative, even for negative fixed point
108
	 * numbers.
109
	 * x.toInt() + x.fractionAsDouble() is approximately equal to x.toDouble()
110
	 */
111
	[[nodiscard]] constexpr double fractionAsDouble() const {
112
		return narrow_cast<double>(value & FRACTION_MASK) * INV_ONE_D;
113
	}
114
115
	// Various arithmetic:
116
117
	/**
118
	 * Returns the result of a division between this fixed point number and
119
	 * another, rounded towards zero.
120
	 */
121
	[[nodiscard]] constexpr int divAsInt(FixedPoint other) const {
122
		return value / other.value;
123
	}
124
125
	/**
126
	 * Returns this value rounded down.
127
	 * The result is equal to FixedPoint(fp.toInt()).
128
	 */
129
	[[nodiscard]] constexpr FixedPoint floor() const {
130
		return create(value & ~FRACTION_MASK);
131
	}
132
133
	/**
134
	 * Returns the fractional part of this value.
135
	 * The result is equal to fp - floor(fp).
136
	 */
137
	[[nodiscard]] constexpr FixedPoint fract() const {
138
		return create(value & FRACTION_MASK);
139
	}
140
141
	/**
142
	 * Returns the fractional part of this value as an integer.
143
	 * The result is equal to  (fract() * (1 << FRACTION_BITS)).toInt()
144
	 */
145
	[[nodiscard]] constexpr unsigned fractAsInt() const {
146
		return value & FRACTION_MASK;
147
	}
148
149
	// Arithmetic operators:
150
151
	[[nodiscard]] constexpr friend FixedPoint operator+(FixedPoint x, FixedPoint y) {
152
		return create(x.value + y.value);
153
	}
154
	[[nodiscard]] constexpr friend FixedPoint operator-(FixedPoint x, FixedPoint y) {
155
		return create(x.value - y.value);
156
	}
157
	[[nodiscard]] constexpr friend FixedPoint operator*(FixedPoint x, FixedPoint y) {
158
		return create(int(
159
			(static_cast<int64_t>(x.value) * y.value) >> FRACTION_BITS));
160
	}
161
	[[nodiscard]] constexpr friend FixedPoint operator*(FixedPoint x, int y) {
162
		return create(x.value * y);
163
	}
164
	[[nodiscard]] constexpr friend FixedPoint operator*(int x, FixedPoint y) {
165
		return create(x * y.value);
166
	}
167
	/**
168
	 * Divides two fixed point numbers.
169
	 * The fractional part is rounded down.
170
	 */
171
	[[nodiscard]] constexpr friend FixedPoint operator/(FixedPoint x, FixedPoint y) {
172
		return create(int(
173
			(static_cast<int64_t>(x.value) << FRACTION_BITS) / y.value));
174
	}
175
	[[nodiscard]] constexpr friend FixedPoint operator/(FixedPoint x, int y) {
176
		return create(x.value / y);
177
	}
178
	[[nodiscard]] constexpr friend FixedPoint operator<<(FixedPoint x, int y) {
179
		return create(x.value << y);
180
	}
181
	[[nodiscard]] constexpr friend FixedPoint operator>>(FixedPoint x, int y) {
182
		return create(x.value >> y);
183
	}
184
185
	// Comparison operators:
186
	[[nodiscard]] constexpr auto operator<=>(const FixedPoint&) const = default;
187
188
	// Arithmetic operators that modify this object:
189
190
	constexpr void operator+=(FixedPoint other) {
191
		value += other.value;
192
	}
193
	constexpr void operator-=(FixedPoint other) {
194
		value -= other.value;
195
	}
196
197
	/** Increase this value with the smallest possible amount. Typically
198
	  * used to implement counters at the resolution of this datatype.
199
	  */
200
	constexpr void addQuantum() {
201
		value += 1;
202
	}
203
204
	// Should only be used by other instances of this class
205
	//  templatized friend declarations are not possible in c++
206
	[[nodiscard]] constexpr int getRawValue() const {
207
		return value;
208
	}
209
210
	template<typename Archive>
211
	void serialize(Archive& ar, unsigned /*version*/)
212
	{
213
		ar.serialize("value", value);
214
	}
215
216
private:
217
	struct CreateRawTag {};
218
	constexpr FixedPoint(int raw_value, CreateRawTag)
219
		: value(raw_value) {}
220
221
	int value = 0;
222
};
223
224
} // namespace openmsx
225
226
#endif // FIXEDPOINT_HH