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