unmoving  v0.1.0
More convenient fixed-point arithmetic for the Sony PlayStation
PSXFixed.hpp
Go to the documentation of this file.
1 
26 #ifndef COM_SAXBOPHONE_UNMOVING_PSX_FIXED_HPP
27 #define COM_SAXBOPHONE_UNMOVING_PSX_FIXED_HPP
28 
29 // we can get int32 and size_t from the C++ standard library when build is hosted
30 #if __STDC_HOSTED__
31 #include <cstddef> // size_t
32 #include <cstdint> // int32
33 #else
34 // NOTE: this C header is specific to the PSX SDK
35 #include <sys/types.h> // int32, size_t
36 #endif
37 /*
38  * get other symbols from the C headers as these are guaranteed to exist both in
39  * C++ and the PSX SDK
40  */
41 #include <stdio.h> // snprintf
42 #include <stdlib.h> // abs
43 
44 namespace unmoving {
45  class PSXFixed; // forward-declaration to allow declaration of user-defined literals
46 
57  constexpr PSXFixed operator"" _fx(long double literal);
58 
73  constexpr PSXFixed operator"" _fx(unsigned long long int literal);
74 
85  class PSXFixed {
86  public:
92  using UnderlyingType = int32_t;
94  static constexpr size_t DECIMAL_BITS = 19;
96  static constexpr size_t FRACTION_BITS = 12;
106  static constexpr double PRECISION = 1.0 / PSXFixed::SCALE;
111  static constexpr double ACCURACY = PSXFixed::PRECISION / 2.0;
115  static constexpr UnderlyingType DECIMAL_MAX = (1 << PSXFixed::DECIMAL_BITS) - 1;
123  static constexpr double FRACTIONAL_MAX = PSXFixed::DECIMAL_MAX + (1.0 - PSXFixed::PRECISION);
127  static constexpr double FRACTIONAL_MIN = PSXFixed::DECIMAL_MIN;
131  static constexpr PSXFixed MAX() {
132  return PSXFixed((UnderlyingType)2147483647);
133  }
137  static constexpr PSXFixed MIN() {
138  return PSXFixed((UnderlyingType)-2147483648);
139  }
143  constexpr PSXFixed() : _raw_value(0) {}
152  constexpr PSXFixed(UnderlyingType raw_value) : _raw_value(raw_value) {}
165  constexpr PSXFixed(double value) {
166  double scaled = value * PSXFixed::SCALE;
167  // separate into integer and fraction so we can round the fraction
168  UnderlyingType integral = (UnderlyingType)scaled;
169  double remainder = scaled - integral;
170  // there's no rounding function in the PS1 stdlib so round manually
171  if (remainder <= -0.5 or remainder >= 0.5) { // round half to infinity
172  integral += (integral < 0 ? -1 : 1);
173  }
174  this->_raw_value = integral;
175  }
184  static constexpr PSXFixed from_integer(int value) {
185  return PSXFixed(value << PSXFixed::FRACTION_BITS);
186  }
190  constexpr operator UnderlyingType() const {
191  return this->_raw_value;
192  }
202  explicit constexpr operator double() const {
203  return (double)this->_raw_value / PSXFixed::SCALE;
204  }
216  explicit constexpr operator float() const {
217  // reuse cast to double and then narrow it to float
218  return (float)(double)*this;
219  }
223  constexpr UnderlyingType to_integer() const {
224  // can't use a right-shift here due to it not handling negative values properly
225  return this->_raw_value / PSXFixed::SCALE;
226  }
242  constexpr bool to_c_str(char* buffer, size_t buffer_size) const {
243  // don't write to a null-pointer!
244  if (buffer == nullptr) { return false; }
245  // need at least 14 characters and 1 for the null-terminator
246  if (buffer_size < 15) { return false; } // refuse if not at least this many in buffer
247  int decimal_part = this->_raw_value / 4096; // floor-divide
248  // decompose the fractional part into an unsigned int to allow us to scale it up for more decimal places
249  unsigned int remainder = ((unsigned int)abs(this->_raw_value)) % 4096;
250  // 1M is the maximum we can scale it up without overflow, since 4096*1M = 4096M, compared to max uint32 = ~4294M
251  unsigned int fractional_part = (remainder * 1'000'000) / 4096; // this gives us 6 decimal places
252  // can't print a negative sign if negative but decimal_part is zero
253  if (this->_raw_value < 0 and decimal_part == 0) {
254  snprintf(buffer, buffer_size, "-0.%06u", fractional_part);
255  } else { // otherwise, we can rely on snprintf() to do it for us
256  snprintf(buffer, buffer_size, "%d.%06u", decimal_part, fractional_part);
257  }
258  return true;
259  }
263  constexpr PSXFixed& operator++() {
264  *this += PSXFixed::SCALE;
265  return *this;
266  }
270  constexpr PSXFixed& operator--() {
271  *this -= PSXFixed::SCALE;
272  return *this;
273  }
277  constexpr PSXFixed operator++(int) {
278  PSXFixed old = *this; // copy old value
279  ++*this; // prefix increment
280  return old; // return old value
281  }
285  constexpr PSXFixed operator--(int) {
286  PSXFixed old = *this; // copy old value
287  --*this; // prefix decrement
288  return old; // return old value
289  }
293  constexpr PSXFixed& operator +=(const PSXFixed& rhs) {
294  this->_raw_value += rhs._raw_value;
295  return *this;
296  }
300  constexpr PSXFixed& operator -=(const PSXFixed& rhs) {
301  this->_raw_value -= rhs._raw_value;
302  return *this;
303  }
312  constexpr PSXFixed& operator *=(const PSXFixed& rhs) {
313  // XXX: no int64_t on PS1, software emulation kicks in automatically
314  int64_t result = (int64_t)this->_raw_value * rhs._raw_value;
315  // shift back down
316  this->_raw_value = (UnderlyingType)(result / PSXFixed::SCALE);
317  return *this;
318  }
323  constexpr PSXFixed& operator *=(const UnderlyingType& rhs) {
324  this->_raw_value *= rhs;
325  return *this;
326  }
335  constexpr PSXFixed& operator /=(const PSXFixed& rhs) {
336  // XXX: no int64_t on PS1, software emulation kicks in automatically
337  int64_t scaled = (int64_t)this->_raw_value * PSXFixed::SCALE;
338  this->_raw_value = (UnderlyingType)(scaled / rhs._raw_value);
339  return *this;
340  }
345  constexpr PSXFixed& operator /=(const UnderlyingType& rhs) {
346  this->_raw_value /= rhs;
347  return *this;
348  }
352  constexpr PSXFixed operator-() const {
353  return PSXFixed(-this->_raw_value);
354  }
358  constexpr friend PSXFixed operator+(PSXFixed lhs, const PSXFixed& rhs) {
359  lhs += rhs;
360  return lhs;
361  }
365  constexpr friend PSXFixed operator-(PSXFixed lhs, const PSXFixed& rhs) {
366  lhs -= rhs;
367  return lhs;
368  }
372  constexpr friend PSXFixed operator*(PSXFixed lhs, const PSXFixed& rhs) {
373  lhs *= rhs;
374  return lhs;
375  }
379  constexpr friend PSXFixed operator*(PSXFixed lhs, const UnderlyingType& rhs) {
380  lhs *= rhs;
381  return lhs;
382  }
386  constexpr friend PSXFixed operator*(UnderlyingType lhs, const PSXFixed& rhs) {
387  return rhs * lhs;
388  }
392  constexpr friend PSXFixed operator/(PSXFixed lhs, const PSXFixed& rhs) {
393  lhs /= rhs;
394  return lhs;
395  }
399  constexpr friend PSXFixed operator/(PSXFixed lhs, const UnderlyingType& rhs) {
400  lhs /= rhs;
401  return lhs;
402  }
403 
404  private:
405  UnderlyingType _raw_value;
406  };
407 
408  constexpr PSXFixed operator"" _fx(long double literal) {
409  return PSXFixed((double)literal);
410  }
411 
412  constexpr PSXFixed operator"" _fx(unsigned long long int literal) {
414  }
415 }
416 
417 #endif // include guard
PSXFixed-point arithmetic value type for Sony PlayStation.
Definition: PSXFixed.hpp:85
static constexpr double FRACTIONAL_MAX
Largest real value representable by the fixed-point type.
Definition: PSXFixed.hpp:123
constexpr UnderlyingType to_integer() const
Definition: PSXFixed.hpp:223
constexpr PSXFixed & operator-=(const PSXFixed &rhs)
Compound assignment subtraction operator.
Definition: PSXFixed.hpp:300
int32_t UnderlyingType
Underlying base type the fixed-point integer is stored as.
Definition: PSXFixed.hpp:92
constexpr PSXFixed & operator*=(const PSXFixed &rhs)
Compound assignment multiplication operator.
Definition: PSXFixed.hpp:312
static constexpr PSXFixed MIN()
Smallest PSXFixed value.
Definition: PSXFixed.hpp:137
constexpr bool to_c_str(char *buffer, size_t buffer_size) const
Stringifies the PSXFixed-point value to a C-string.
Definition: PSXFixed.hpp:242
static constexpr double FRACTIONAL_MIN
Smallest real value representable by the fixed-point type.
Definition: PSXFixed.hpp:127
static constexpr PSXFixed MAX()
Largest PSXFixed value.
Definition: PSXFixed.hpp:131
constexpr PSXFixed(double value)
Implicit converting constructor from float/double.
Definition: PSXFixed.hpp:165
static constexpr UnderlyingType DECIMAL_MAX
Largest integer value representable by the fixed-point type.
Definition: PSXFixed.hpp:115
constexpr friend PSXFixed operator*(PSXFixed lhs, const UnderlyingType &rhs)
Integer multiplication operator.
Definition: PSXFixed.hpp:379
constexpr PSXFixed operator++(int)
Postfix increment operator.
Definition: PSXFixed.hpp:277
constexpr friend PSXFixed operator-(PSXFixed lhs, const PSXFixed &rhs)
Subtraction operator.
Definition: PSXFixed.hpp:365
constexpr friend PSXFixed operator/(PSXFixed lhs, const PSXFixed &rhs)
Division operator.
Definition: PSXFixed.hpp:392
static constexpr double ACCURACY
The largest difference between a fixed-point value and the "true" value it represents.
Definition: PSXFixed.hpp:111
constexpr PSXFixed(UnderlyingType raw_value)
Implicit converting constructor from fixed-point integer.
Definition: PSXFixed.hpp:152
constexpr friend PSXFixed operator+(PSXFixed lhs, const PSXFixed &rhs)
Addition operator.
Definition: PSXFixed.hpp:358
static constexpr double PRECISION
How far apart two adjacent fixed-point values are.
Definition: PSXFixed.hpp:106
constexpr friend PSXFixed operator*(UnderlyingType lhs, const PSXFixed &rhs)
Integer multiplication operator.
Definition: PSXFixed.hpp:386
constexpr PSXFixed & operator++()
Prefix increment operator.
Definition: PSXFixed.hpp:263
static constexpr size_t DECIMAL_BITS
How many bits are used for the integer part of the fixed-point integer.
Definition: PSXFixed.hpp:94
constexpr PSXFixed & operator+=(const PSXFixed &rhs)
Compound assignment addition operator.
Definition: PSXFixed.hpp:293
static constexpr size_t FRACTION_BITS
How many bits are used for the fraction part of the fixed-point integer.
Definition: PSXFixed.hpp:96
constexpr PSXFixed()
Default constructor, creates a PSXFixed instance with value 0.0_fx
Definition: PSXFixed.hpp:143
constexpr PSXFixed operator--(int)
Postfix decrement operator.
Definition: PSXFixed.hpp:285
constexpr friend PSXFixed operator*(PSXFixed lhs, const PSXFixed &rhs)
Multiplication operator.
Definition: PSXFixed.hpp:372
constexpr PSXFixed & operator/=(const PSXFixed &rhs)
Compound assignment division operator.
Definition: PSXFixed.hpp:335
static constexpr PSXFixed from_integer(int value)
Definition: PSXFixed.hpp:184
static constexpr UnderlyingType SCALE
The scale used for the fixed-point integer.
Definition: PSXFixed.hpp:102
constexpr PSXFixed & operator--()
Prefix decrement operator.
Definition: PSXFixed.hpp:270
constexpr friend PSXFixed operator/(PSXFixed lhs, const UnderlyingType &rhs)
Integer division operator.
Definition: PSXFixed.hpp:399
static constexpr UnderlyingType DECIMAL_MIN
Smallest integer value representable by the fixed-point type.
Definition: PSXFixed.hpp:119
constexpr PSXFixed operator-() const
Unary minus (negation) operator.
Definition: PSXFixed.hpp:352
Definition: PSXFixed.hpp:44