1 // Copyright 2015-2018 the openage authors. See copying.md for legal info.
2
3 #pragma once
4
5 #include <climits>
6 #include <cmath>
7 #include <iomanip>
8 #include <limits>
9 #include <ostream>
10 #include <type_traits>
11
12 #include "misc.h"
13
14 namespace openage {
15 namespace util {
16
17
18 /**
19 * Helper function that performs a left shift without causing undefined
20 * behavior.
21 * regular left-shift is undefined if amount >= bitwidth,
22 * or amount >= bitwidth - 1 for signed integers.
23 */
24 template<unsigned int amount, typename T>
25 constexpr static
26 typename std::enable_if<(amount + (std::is_signed<T>::value ? 1 : 0) < sizeof(T) * CHAR_BIT), T>::type
safe_shiftleft(T value)27 safe_shiftleft(T value) {
28 return static_cast<T>(
29 static_cast<typename std::make_unsigned<T>::type>(value) << amount
30 );
31 }
32
33
34 /**
35 * Helper function that performs a right shift without causing undefined
36 * behavior.
37 * right-shift is usually undefined if amount >= bit size.
38 */
39 template<unsigned int amount, typename T>
40 constexpr static
41 typename std::enable_if<(amount >= sizeof(T) * CHAR_BIT), T>::type
safe_shiftright(T value)42 safe_shiftright(T value) {
43 return value < 0 ? -1 : 0;
44 }
45
46 template<unsigned int amount, typename T>
47 constexpr static
48 typename std::enable_if<(amount < sizeof(T) * CHAR_BIT), T>::type
safe_shiftright(T value)49 safe_shiftright(T value) {
50 return value >> amount;
51 }
52
53
54 /**
55 * Helper function that performs either a safe shift-right (amount > 0),
56 * or a safe shift-left (amount < 0).
57 */
58 template<int amount, typename T>
59 constexpr static
60 typename std::enable_if<(amount < 0), T>::type
safe_shift(T value)61 safe_shift(T value) {
62 return safe_shiftright<-amount>(value);
63 }
64
65
66 template<int amount, typename T>
67 constexpr static
68 typename std::enable_if<(amount >= 0), T>::type
safe_shift(T value)69 safe_shift(T value) {
70 return safe_shiftleft<amount>(value);
71 }
72
73
74 /**
75 * Fixed-point integer class;
76 *
77 * this is designed to be used instead of floats in places where guaranteed
78 * precision is required.
79 *
80 * For example,
81 * FixedPoint<int64_t, 32>
82 * can store values from -2**32 to +2**32 with a constant precision of 2**-32.
83 */
84 template<typename int_type, unsigned int fractional_bits>
85 class FixedPoint {
86 private:
87 // Helper function to create the scaling factors that are used below.
power_of_two(unsigned int power)88 static constexpr double power_of_two(unsigned int power) {
89 double result = 1.0;
90 while (power--) {
91 result *= 2.0;
92 }
93 return result;
94 }
95
96 int_type raw_value;
97
98 static constexpr const double from_double_factor = power_of_two(fractional_bits);
99 static constexpr const double to_double_factor = 1 / from_double_factor;
100 static constexpr const float from_float_factor = from_double_factor;
101 static constexpr const float to_float_factor = to_double_factor;
102
103 static constexpr const unsigned int approx_decimal_places = static_cast<unsigned int>(
104 static_cast<double>(fractional_bits) * 0.30103 + 1
105 );
106
107 using this_type = FixedPoint<int_type, fractional_bits>;
108 using unsigned_int_type = typename std::make_unsigned<int_type>::type;
109 using same_type_but_unsigned = FixedPoint<typename FixedPoint::unsigned_int_type, fractional_bits>;
110
111 // constexpr helper function for get_fractional_part()
fractional_part_bitmask()112 static constexpr typename FixedPoint::unsigned_int_type fractional_part_bitmask() {
113 // return ~(MAX_VAL << fractional_bits);
114 return static_cast<FixedPoint::unsigned_int_type>(
115 ~(
116 safe_shiftleft<fractional_bits, FixedPoint::unsigned_int_type>(
117 std::numeric_limits<FixedPoint::unsigned_int_type>::max()
118 )));
119 }
120
121 friend std::hash<openage::util::FixedPoint<int_type, fractional_bits>>;
122
raw_value_from_double(double n)123 static constexpr int_type raw_value_from_double(double n) {
124 return static_cast<int_type>(n * from_double_factor);
125 }
126
127 public:
128 // obligatory copy constructor / assignment operator.
FixedPoint(const FixedPoint & other)129 constexpr FixedPoint(const FixedPoint &other) : raw_value(other.raw_value) {}
130
131 constexpr FixedPoint &operator =(const FixedPoint &other) {
132 raw_value = other.raw_value;
133 return *this;
134 }
135
136 /**
137 * Empty constructor. Initializes the number to 0.
138 */
FixedPoint()139 constexpr FixedPoint() : raw_value(0) {}
140
141 /**
142 * floating-point constructor. Initializes the number from a double.
143 */
144 // implicitly construct from double.
145 // for other creations, use the factory methods below.
FixedPoint(double n)146 constexpr FixedPoint(double n) : raw_value(FixedPoint::raw_value_from_double(n)) {}
147
148 /**
149 * FixedPoint value that is preinitialized to zero.
150 */
zero()151 static constexpr FixedPoint zero() {
152 return FixedPoint::from_int(0);
153 }
154
155 /**
156 * Factory function to get a fixed-point number from an integer.
157 */
from_int(int_type n)158 static constexpr FixedPoint from_int(int_type n) {
159 return FixedPoint::from_raw_value(safe_shiftleft<fractional_bits, int_type>(n));
160 }
161
162 /**
163 * Factory function to get a fixed-point number from a float.
164 */
from_float(float n)165 static constexpr FixedPoint from_float(float n) {
166 return FixedPoint::from_raw_value(static_cast<int_type>(n * from_float_factor));
167 }
168
169 /**
170 * Factory function to get a fixed-point number from a double.
171 */
from_double(double n)172 static constexpr FixedPoint from_double(double n) {
173 return FixedPoint::from_raw_value(FixedPoint::raw_value_from_double(n));
174 }
175
176 /**
177 * Factory function to get a fixed-point number from a fixed-point number of different type.
178 */
179 template<typename other_int_type, unsigned int other_fractional_bits>
from_fixedpoint(const FixedPoint<other_int_type,other_fractional_bits> & other)180 static constexpr FixedPoint from_fixedpoint(const FixedPoint<other_int_type, other_fractional_bits> &other) {
181 return FixedPoint::from_raw_value(
182 safe_shift<fractional_bits - other_fractional_bits, int_type>(other.get_raw_value())
183 );
184 }
185
186 /**
187 * The minimum possible value of this type.
188 */
min_value()189 static constexpr const FixedPoint min_value() {
190 return FixedPoint::from_raw_value(std::numeric_limits<int_type>::min());
191 }
192
193 /**
194 * The maximum possible value of this type.
195 */
max_value()196 static constexpr const FixedPoint max_value() {
197 return FixedPoint::from_raw_value(std::numeric_limits<int_type>::max());
198 }
199
200 /**
201 * Factory function to construct a fixed-point number with a given raw value.
202 * Don't use this.
203 */
from_raw_value(int_type raw_value)204 static constexpr FixedPoint from_raw_value(int_type raw_value) {
205 FixedPoint result;
206 result.raw_value = raw_value;
207 return result;
208 }
209
210 /**
211 * Converter to retrieve the raw value of the fixed-point number.
212 * Don't use this.
213 */
get_raw_value()214 constexpr int_type get_raw_value() const {
215 return this->raw_value;
216 }
217
218 /**
219 * Converter to retrieve the int (pre-decimal) part of the number.
220 */
to_int()221 constexpr int_type to_int() const {
222 return safe_shiftright<fractional_bits, int_type>(this->raw_value);
223 }
224
225 constexpr explicit operator int() const {
226 return this->to_int();
227 }
228
229 /**
230 * Converter to retrieve the number as float.
231 */
to_float()232 constexpr float to_float() const {
233 return static_cast<float>(this->raw_value) * FixedPoint::to_float_factor;
234 }
235
236 constexpr explicit operator float() const {
237 return this->to_float();
238 }
239
240 /**
241 * Converter to retrieve the number as double.
242 */
to_double()243 constexpr double to_double() const {
244 return static_cast<double>(this->raw_value) * FixedPoint::to_double_factor;
245 }
246
247 constexpr explicit operator double() const {
248 return this->to_double();
249 }
250
251 /**
252 * Converter to retrieve the fractional (post-decimal) part of the number.
253 */
get_fractional_part()254 constexpr typename FixedPoint::same_type_but_unsigned get_fractional_part() const {
255 // returns a new variable with only the bits from
256 // fractional_part_bitmask set.
257 return FixedPoint::same_type_but_unsigned::from_raw_value(
258 static_cast<FixedPoint::unsigned_int_type>(this->raw_value) &
259 std::integral_constant<
260 int_type,
261 FixedPoint::fractional_part_bitmask()
262 >::value
263 );
264 }
265
266 // Comparison operators for comparison with other
267 constexpr bool operator ==(const FixedPoint &o) const {
268 return raw_value == o.raw_value;
269 }
270
271 constexpr bool operator !=(const FixedPoint &o) const {
272 return raw_value != o.raw_value;
273 }
274
275 constexpr bool operator <(const FixedPoint &o) const {
276 return raw_value < o.raw_value;
277 }
278
279 constexpr bool operator >(const FixedPoint &o) const {
280 return raw_value > o.raw_value;
281 }
282
283 constexpr bool operator <=(const FixedPoint &o) const {
284 return raw_value <= o.raw_value;
285 }
286
287 constexpr bool operator >=(const FixedPoint &o) const {
288 return raw_value >= o.raw_value;
289 }
290
291 // Unary operators
292 constexpr FixedPoint operator +() const {
293 return *this;
294 }
295
296 // the inner_int_type template is required for enable_if.
297 template <typename inner_int_type=int_type>
298 constexpr
299 typename std::enable_if<std::is_signed<inner_int_type>::value, typename FixedPoint::this_type>::type
300 operator -() const {
301 static_assert(std::is_same<inner_int_type, int_type>::value, "inner_int_type must == int_type");
302 return FixedPoint::this_type::from_raw_value(-this->raw_value);
303 }
304
305 template<typename I, unsigned F>
hypot(const FixedPoint<I,F> rhs)306 constexpr double hypot(const FixedPoint<I, F> rhs) {
307 return std::hypot(this->to_double(), rhs.to_double());
308 }
309
310 template<typename I, unsigned F>
hypotfp(const FixedPoint<I,F> rhs)311 constexpr FixedPoint<I, F> hypotfp(const FixedPoint<I, F> rhs) {
312 return FixedPoint<I, F>(this->hypot(rhs));
313 }
314
315 // Basic operators
316 constexpr FixedPoint &operator +=(const FixedPoint &n) {
317 this->raw_value += n.raw_value;
318 return *this;
319 }
320
321 constexpr FixedPoint &operator -=(const FixedPoint &n) {
322 this->raw_value -= n.raw_value;
323 return *this;
324 }
325
swap(FixedPoint & rhs)326 void swap(FixedPoint &rhs) {
327 std::swap(this->raw_value, rhs.raw_value);
328 }
329
330 // I/O operators
331 friend std::ostream &operator <<(std::ostream &os, const FixedPoint &n) {
332 os << std::fixed << std::setprecision(FixedPoint::approx_decimal_places) << double(n);
333 return os;
334 }
335
336 friend std::istream &operator >>(std::istream &is, FixedPoint &n) {
337 double temp;
338 is >> temp;
339 n = temp;
340 return is;
341 }
342
sqrt()343 constexpr double sqrt() {
344 return std::sqrt(this->to_double());
345 }
346 };
347
348
349 // Binary operators
350
351 /**
352 * FixedPoint + FixedPoint
353 */
354 template<typename I, unsigned int F>
355 constexpr FixedPoint<I, F> operator +(const FixedPoint<I, F> &lhs, const FixedPoint<I, F> &rhs) {
356 return FixedPoint<I, F>::from_raw_value(lhs.get_raw_value() + rhs.get_raw_value());
357 }
358
359 /**
360 * FixedPoint - FixedPoint
361 */
362 template<typename I, unsigned int F>
363 constexpr FixedPoint<I, F> operator -(const FixedPoint<I, F> &lhs, const FixedPoint<I, F> &rhs) {
364 return FixedPoint<I, F>::from_raw_value(lhs.get_raw_value() - rhs.get_raw_value());
365 }
366
367
368 /**
369 * FixedPoint * N
370 */
371 template<typename I, unsigned F, typename N>
372 typename std::enable_if<std::is_arithmetic<N>::value, FixedPoint<I, F>>::type
373 constexpr operator *(const FixedPoint<I, F> lhs, const N &rhs) {
374 return FixedPoint<I, F>::from_raw_value(lhs.get_raw_value() * rhs);
375 }
376
377 /*
378 FixedPoint * FixedPoint is missing to prevent surprising overflows.
379
380 using fp = FixedPoint<uint64_t, 16>;
381 fp a = fp.from_int(1 << 16);
382 => a * a will overflow because:
383 a.rawvalue == 2^(16+16) == 2^32
384 -> a.rawvalue * a.rawvalue == 2^64 => pwnt
385 */
386
387
388 /**
389 * FixedPoint / N
390 */
391 template<typename I, unsigned F, typename N>
392 constexpr FixedPoint<I, F> operator /(const FixedPoint<I, F> lhs, const N &rhs) {
393 return FixedPoint<I, F>::from_raw_value(div(lhs.get_raw_value(), static_cast<I>(rhs)));
394 }
395
396 }} // namespace openage::util
397
398
399 // std function overloads
400 namespace std {
401
402 template<typename I, unsigned F>
sqrt(openage::util::FixedPoint<I,F> n)403 constexpr double sqrt(openage::util::FixedPoint<I, F> n) {
404 return n.sqrt();
405 }
406
407 template<typename I, unsigned F>
min(openage::util::FixedPoint<I,F> x,openage::util::FixedPoint<I,F> y)408 constexpr openage::util::FixedPoint<I, F> min(openage::util::FixedPoint<I, F> x, openage::util::FixedPoint<I, F> y) {
409 return openage::util::FixedPoint<I, F>::from_raw_value(
410 std::min(x.get_raw_value(),
411 y.get_raw_value())
412 );
413 }
414
415 template<typename I, unsigned F>
max(openage::util::FixedPoint<I,F> x,openage::util::FixedPoint<I,F> y)416 constexpr openage::util::FixedPoint<I, F> max(openage::util::FixedPoint<I, F> x, openage::util::FixedPoint<I, F> y) {
417 return openage::util::FixedPoint<I, F>::from_raw_value(
418 std::max(x.get_raw_value(),
419 y.get_raw_value())
420 );
421 }
422
423 template<typename I, unsigned F>
abs(openage::util::FixedPoint<I,F> n)424 constexpr openage::util::FixedPoint<I, F> abs(openage::util::FixedPoint<I, F> n) {
425 return openage::util::FixedPoint<I, F>::from_raw_value(
426 std::abs(n.get_raw_value())
427 );
428 }
429
430 template<typename I, unsigned F>
hypot(openage::util::FixedPoint<I,F> x,openage::util::FixedPoint<I,F> y)431 constexpr double hypot(openage::util::FixedPoint<I, F> x, openage::util::FixedPoint<I, F> y) {
432 return x.hypot(y);
433 }
434
435 template<typename I, unsigned F>
436 struct hash<openage::util::FixedPoint<I, F>> {
437 constexpr size_t operator ()(const openage::util::FixedPoint<I, F> &n) const {
438 return std::hash<I>{}(n.raw_value);
439 }
440 };
441
442 } // namespace std
443