Fixed.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017 Louis Langholtz https://github.com/louis-langholtz/PlayRho
3  *
4  * This software is provided 'as-is', without any express or implied
5  * warranty. In no event will the authors be held liable for any damages
6  * arising from the use of this software.
7  *
8  * Permission is granted to anyone to use this software for any purpose,
9  * including commercial applications, and to alter it and redistribute it
10  * freely, subject to the following restrictions:
11  *
12  * 1. The origin of this software must not be misrepresented; you must not
13  * claim that you wrote the original software. If you use this software
14  * in a product, an acknowledgment in the product documentation would be
15  * appreciated but is not required.
16  * 2. Altered source versions must be plainly marked as such, and must not be
17  * misrepresented as being the original software.
18  * 3. This notice may not be removed or altered from any source distribution.
19  */
20 
21 #ifndef PLAYRHO_COMMON_FIXED_HPP
22 #define PLAYRHO_COMMON_FIXED_HPP
23 
24 #include <PlayRho/Common/Wider.hpp>
26 
27 #include <cstdint>
28 #include <limits>
29 #include <cassert>
30 #include <type_traits>
31 #include <iostream>
32 
33 namespace playrho {
34 
43  template <typename BASE_TYPE, unsigned int FRACTION_BITS>
44  class Fixed
45  {
46  public:
47 
49  using value_type = BASE_TYPE;
50 
52  static PLAYRHO_CONSTEXPR const unsigned int TotalBits = sizeof(BASE_TYPE) * 8;
53 
55  static PLAYRHO_CONSTEXPR const unsigned int FractionBits = FRACTION_BITS;
56 
58  static PLAYRHO_CONSTEXPR const unsigned int WholeBits = TotalBits - FractionBits;
59 
61  static PLAYRHO_CONSTEXPR const value_type ScaleFactor = static_cast<value_type>(1u << FractionBits);
62 
64  enum class CmpResult
65  {
67  Equal,
68  LessThan,
70  };
71 
73  static PLAYRHO_CONSTEXPR inline Fixed GetMin() noexcept
74  {
75  return Fixed{1, scalar_type{1}};
76  }
77 
79  static PLAYRHO_CONSTEXPR inline Fixed GetInfinity() noexcept
80  {
81  return Fixed{numeric_limits::max(), scalar_type{1}};
82  }
83 
85  static PLAYRHO_CONSTEXPR inline Fixed GetMax() noexcept
86  {
87  // max reserved for +inf
88  return Fixed{numeric_limits::max() - 1, scalar_type{1}};
89  }
90 
92  static PLAYRHO_CONSTEXPR inline Fixed GetNaN() noexcept
93  {
94  return Fixed{numeric_limits::lowest(), scalar_type{1}};
95  }
96 
98  static PLAYRHO_CONSTEXPR inline Fixed GetNegativeInfinity() noexcept
99  {
100  // lowest reserved for NaN
101  return Fixed{numeric_limits::lowest() + 1, scalar_type{1}};
102  }
103 
105  static PLAYRHO_CONSTEXPR inline Fixed GetLowest() noexcept
106  {
107  // lowest reserved for NaN
108  // lowest + 1 reserved for -inf
109  return Fixed{numeric_limits::lowest() + 2, scalar_type{1}};
110  }
111 
113  template <typename T>
114  static PLAYRHO_CONSTEXPR inline value_type GetFromFloat(T val) noexcept
115  {
116  static_assert(std::is_floating_point<T>::value, "floating point value required");
117  // Note: std::isnan(val) *NOT* constant expression, so can't use here!
118  return !(val <= 0 || val >= 0)? GetNaN().m_value:
119  (val > static_cast<long double>(GetMax()))? GetInfinity().m_value:
120  (val < static_cast<long double>(GetLowest()))? GetNegativeInfinity().m_value:
121  static_cast<value_type>(val * ScaleFactor);
122  }
123 
125  template <typename T>
126  static PLAYRHO_CONSTEXPR inline value_type GetFromSignedInt(T val) noexcept
127  {
128  static_assert(std::is_integral<T>::value, "integral value required");
129  static_assert(std::is_signed<T>::value, "must be signed");
130  return (val > (GetMax().m_value / ScaleFactor))? GetInfinity().m_value:
131  (val < (GetLowest().m_value / ScaleFactor))? GetNegativeInfinity().m_value:
132  static_cast<value_type>(val * ScaleFactor);
133  }
134 
136  template <typename T>
137  static PLAYRHO_CONSTEXPR inline value_type GetFromUnsignedInt(T val) noexcept
138  {
139  static_assert(std::is_integral<T>::value, "integral value required");
140  static_assert(!std::is_signed<T>::value, "must be unsigned");
141  const auto max = static_cast<unsigned_wider_type>(GetMax().m_value / ScaleFactor);
142  return (val > max)? GetInfinity().m_value: static_cast<value_type>(val) * ScaleFactor;
143  }
144 
145  Fixed() = default;
146 
148  PLAYRHO_CONSTEXPR inline Fixed(long double val) noexcept:
149  m_value{GetFromFloat(val)}
150  {
151  // Intentionally empty
152  }
153 
155  PLAYRHO_CONSTEXPR inline Fixed(double val) noexcept:
156  m_value{GetFromFloat(val)}
157  {
158  // Intentionally empty
159  }
160 
162  PLAYRHO_CONSTEXPR inline Fixed(float val) noexcept:
163  m_value{GetFromFloat(val)}
164  {
165  // Intentionally empty
166  }
167 
169  PLAYRHO_CONSTEXPR inline Fixed(unsigned long long val) noexcept:
170  m_value{GetFromUnsignedInt(val)}
171  {
172  // Intentionally empty.
173  }
174 
176  PLAYRHO_CONSTEXPR inline Fixed(unsigned long val) noexcept:
177  m_value{GetFromUnsignedInt(val)}
178  {
179  // Intentionally empty.
180  }
181 
183  PLAYRHO_CONSTEXPR inline Fixed(unsigned int val) noexcept:
184  m_value{GetFromUnsignedInt(val)}
185  {
186  // Intentionally empty.
187  }
188 
190  PLAYRHO_CONSTEXPR inline Fixed(long long val) noexcept:
191  m_value{GetFromSignedInt(val)}
192  {
193  // Intentionally empty.
194  }
195 
197  PLAYRHO_CONSTEXPR inline Fixed(long val) noexcept:
198  m_value{GetFromSignedInt(val)}
199  {
200  // Intentionally empty.
201  }
202 
204  PLAYRHO_CONSTEXPR inline Fixed(int val) noexcept:
205  m_value{GetFromSignedInt(val)}
206  {
207  // Intentionally empty.
208  }
209 
211  PLAYRHO_CONSTEXPR inline Fixed(short val) noexcept:
212  m_value{GetFromSignedInt(val)}
213  {
214  // Intentionally empty.
215  }
216 
218  PLAYRHO_CONSTEXPR inline Fixed(value_type val, unsigned int fraction) noexcept:
219  m_value{static_cast<value_type>(static_cast<std::uint32_t>(val * ScaleFactor) | fraction)}
220  {
221  // Intentionally empty.
222  }
223 
225  template <typename BT, unsigned int FB>
226  PLAYRHO_CONSTEXPR inline Fixed(const Fixed<BT, FB> val) noexcept:
227  Fixed(static_cast<long double>(val))
228  {
229  // Intentionally empty
230  }
231 
232  // Methods
233 
235  template <typename T>
236  PLAYRHO_CONSTEXPR inline T ConvertTo() const noexcept
237  {
238  return isnan()? std::numeric_limits<T>::signaling_NaN():
239  !isfinite()? std::numeric_limits<T>::infinity() * getsign():
240  m_value / static_cast<T>(ScaleFactor);
241  }
242 
244  PLAYRHO_CONSTEXPR inline CmpResult Compare(const Fixed other) const noexcept
245  {
246  if (isnan() || other.isnan())
247  {
249  }
250  if (m_value < other.m_value)
251  {
252  return CmpResult::LessThan;
253  }
254  if (m_value > other.m_value)
255  {
256  return CmpResult::GreaterThan;
257  }
258  return CmpResult::Equal;
259  }
260 
261  // Unary operations
262 
264  explicit PLAYRHO_CONSTEXPR inline operator long double() const noexcept
265  {
266  return ConvertTo<long double>();
267  }
268 
270  explicit PLAYRHO_CONSTEXPR inline operator double() const noexcept
271  {
272  return ConvertTo<double>();
273  }
274 
276  explicit PLAYRHO_CONSTEXPR inline operator float() const noexcept
277  {
278  return ConvertTo<float>();
279  }
280 
282  explicit PLAYRHO_CONSTEXPR inline operator long long() const noexcept
283  {
284  return m_value / ScaleFactor;
285  }
286 
288  explicit PLAYRHO_CONSTEXPR inline operator long() const noexcept
289  {
290  return m_value / ScaleFactor;
291  }
292 
294  explicit PLAYRHO_CONSTEXPR inline operator unsigned long long() const noexcept
295  {
296  // Behavior is undefined if m_value is negative
297  return static_cast<unsigned long long>(m_value / ScaleFactor);
298  }
299 
301  explicit PLAYRHO_CONSTEXPR inline operator unsigned long() const noexcept
302  {
303  // Behavior is undefined if m_value is negative
304  return static_cast<unsigned long>(m_value / ScaleFactor);
305  }
306 
308  explicit PLAYRHO_CONSTEXPR inline operator unsigned int() const noexcept
309  {
310  // Behavior is undefined if m_value is negative
311  return static_cast<unsigned int>(m_value / ScaleFactor);
312  }
313 
315  explicit PLAYRHO_CONSTEXPR inline operator int() const noexcept
316  {
317  return static_cast<int>(m_value / ScaleFactor);
318  }
319 
321  explicit PLAYRHO_CONSTEXPR inline operator short() const noexcept
322  {
323  return static_cast<short>(m_value / ScaleFactor);
324  }
325 
327  PLAYRHO_CONSTEXPR inline Fixed operator- () const noexcept
328  {
329  return (isnan())? *this: Fixed{-m_value, scalar_type{1}};
330  }
331 
333  PLAYRHO_CONSTEXPR inline Fixed operator+ () const noexcept
334  {
335  return *this;
336  }
337 
339  explicit PLAYRHO_CONSTEXPR inline operator bool() const noexcept
340  {
341  return m_value != 0;
342  }
343 
345  PLAYRHO_CONSTEXPR inline bool operator! () const noexcept
346  {
347  return m_value == 0;
348  }
349 
351  PLAYRHO_CONSTEXPR inline Fixed& operator+= (Fixed val) noexcept
352  {
353  if (isnan() || val.isnan()
354  || ((m_value == GetInfinity().m_value) && (val.m_value == GetNegativeInfinity().m_value))
355  || ((m_value == GetNegativeInfinity().m_value) && (val.m_value == GetInfinity().m_value))
356  )
357  {
358  *this = GetNaN();
359  }
360  else if (val.m_value == GetInfinity().m_value)
361  {
362  m_value = GetInfinity().m_value;
363  }
364  else if (val.m_value == GetNegativeInfinity().m_value)
365  {
366  m_value = GetNegativeInfinity().m_value;
367  }
368  else if (isfinite() && val.isfinite())
369  {
370  const auto result = wider_type{m_value} + val.m_value;
371  if (result > GetMax().m_value)
372  {
373  // overflow from max
374  m_value = GetInfinity().m_value;
375  }
376  else if (result < GetLowest().m_value)
377  {
378  // overflow from lowest
379  m_value = GetNegativeInfinity().m_value;
380  }
381  else
382  {
383  m_value = static_cast<value_type>(result);
384  }
385  }
386  return *this;
387  }
388 
390  PLAYRHO_CONSTEXPR inline Fixed& operator-= (Fixed val) noexcept
391  {
392  if (isnan() || val.isnan()
393  || ((m_value == GetInfinity().m_value) && (val.m_value == GetInfinity().m_value))
394  || ((m_value == GetNegativeInfinity().m_value) && (val.m_value == GetNegativeInfinity().m_value))
395  )
396  {
397  *this = GetNaN();
398  }
399  else if (val.m_value == GetInfinity().m_value)
400  {
401  m_value = GetNegativeInfinity().m_value;
402  }
403  else if (val.m_value == GetNegativeInfinity().m_value)
404  {
405  m_value = GetInfinity().m_value;
406  }
407  else if (isfinite() && val.isfinite())
408  {
409  const auto result = wider_type{m_value} - val.m_value;
410  if (result > GetMax().m_value)
411  {
412  // overflow from max
413  m_value = GetInfinity().m_value;
414  }
415  else if (result < GetLowest().m_value)
416  {
417  // overflow from lowest
418  m_value = GetNegativeInfinity().m_value;
419  }
420  else
421  {
422  m_value = static_cast<value_type>(result);
423  }
424  }
425  return *this;
426  }
427 
429  PLAYRHO_CONSTEXPR inline Fixed& operator*= (Fixed val) noexcept
430  {
431  if (isnan() || val.isnan())
432  {
433  *this = GetNaN();
434  }
435  else if (!isfinite() || !val.isfinite())
436  {
437  if (m_value == 0 || val.m_value == 0)
438  {
439  *this = GetNaN();
440  }
441  else
442  {
443  *this = ((m_value > 0) != (val.m_value > 0))? -GetInfinity(): GetInfinity();
444  }
445  }
446  else
447  {
448  const auto product = wider_type{m_value} * wider_type{val.m_value};
449  const auto result = product / ScaleFactor;
450 
451  if (product != 0 && result == 0)
452  {
453  // underflow
454  m_value = static_cast<value_type>(result);
455  }
456  else if (result > GetMax().m_value)
457  {
458  // overflow from max
459  m_value = GetInfinity().m_value;
460  }
461  else if (result < GetLowest().m_value)
462  {
463  // overflow from lowest
464  m_value = GetNegativeInfinity().m_value;
465  }
466  else
467  {
468  m_value = static_cast<value_type>(result);
469  }
470  }
471  return *this;
472  }
473 
475  PLAYRHO_CONSTEXPR inline Fixed& operator/= (Fixed val) noexcept
476  {
477  if (isnan() || val.isnan())
478  {
479  *this = GetNaN();
480  }
481  else if (!isfinite() && !val.isfinite())
482  {
483  *this = GetNaN();
484  }
485  else if (!isfinite())
486  {
487  *this = ((m_value > 0) != (val.m_value > 0))? -GetInfinity(): GetInfinity();
488  }
489  else if (!val.isfinite())
490  {
491  *this = 0;
492  }
493  else
494  {
495  const auto product = wider_type{m_value} * ScaleFactor;
496  const auto result = product / val.m_value;
497 
498  if (product != 0 && result == 0)
499  {
500  // underflow
501  m_value = static_cast<value_type>(result);
502  }
503  else if (result > GetMax().m_value)
504  {
505  // overflow from max
506  m_value = GetInfinity().m_value;
507  }
508  else if (result < GetLowest().m_value)
509  {
510  // overflow from lowest
511  m_value = GetNegativeInfinity().m_value;
512  }
513  else
514  {
515  m_value = static_cast<value_type>(result);
516  }
517  }
518  return *this;
519  }
520 
522  PLAYRHO_CONSTEXPR inline Fixed& operator%= (Fixed val) noexcept
523  {
524  assert(!isnan());
525  assert(!val.isnan());
526 
527  m_value %= val.m_value;
528  return *this;
529  }
530 
532  PLAYRHO_CONSTEXPR inline bool isfinite() const noexcept
533  {
534  return (m_value > GetNegativeInfinity().m_value)
535  && (m_value < GetInfinity().m_value);
536  }
537 
539  PLAYRHO_CONSTEXPR inline bool isnan() const noexcept
540  {
541  return m_value == GetNaN().m_value;
542  }
543 
545  PLAYRHO_CONSTEXPR inline int getsign() const noexcept
546  {
547  return (m_value >= 0)? +1: -1;
548  }
549 
550  private:
551 
553  using wider_type = typename Wider<value_type>::type;
554 
556  using unsigned_wider_type = typename std::make_unsigned<wider_type>::type;
557 
559  struct scalar_type
560  {
561  value_type value = 1;
562  };
563 
565  using numeric_limits = std::numeric_limits<value_type>;
566 
568  PLAYRHO_CONSTEXPR inline Fixed(value_type val, scalar_type scalar) noexcept:
569  m_value{val * scalar.value}
570  {
571  // Intentionally empty.
572  }
573 
574  value_type m_value;
575  };
576 
578  template <typename BT, unsigned int FB>
580  {
581  return lhs.Compare(rhs) == Fixed<BT, FB>::CmpResult::Equal;
582  }
583 
585  template <typename BT, unsigned int FB>
587  {
588  return lhs.Compare(rhs) != Fixed<BT, FB>::CmpResult::Equal;
589  }
590 
592  template <typename BT, unsigned int FB>
594  {
595  return lhs.Compare(rhs) == Fixed<BT, FB>::CmpResult::LessThan;
596  }
597 
599  template <typename BT, unsigned int FB>
601  {
602  return lhs.Compare(rhs) == Fixed<BT, FB>::CmpResult::GreaterThan;
603  }
604 
606  template <typename BT, unsigned int FB>
608  {
609  const auto result = lhs.Compare(rhs);
610  return result == Fixed<BT, FB>::CmpResult::LessThan ||
612  }
613 
615  template <typename BT, unsigned int FB>
617  {
618  const auto result = lhs.Compare(rhs);
620  }
621 
623  template <typename BT, unsigned int FB>
625  {
626  lhs += rhs;
627  return lhs;
628  }
629 
631  template <typename BT, unsigned int FB>
633  {
634  lhs -= rhs;
635  return lhs;
636  }
637 
639  template <typename BT, unsigned int FB>
641  {
642  lhs *= rhs;
643  return lhs;
644  }
645 
647  template <typename BT, unsigned int FB>
649  {
650  lhs /= rhs;
651  return lhs;
652  }
653 
655  template <typename BT, unsigned int FB>
657  {
658  lhs %= rhs;
659  return lhs;
660  }
661 
666  template <typename BT, unsigned int FB>
668  {
669  return value == 0;
670  }
671 
673  template <typename BT, unsigned int FB>
675  {
676  return abs(x - y) <= Fixed<BT, FB>{0, static_cast<std::uint32_t>(ulp)};
677  }
678 
679 #ifdef CONFLICT_WITH_GETINVALID
680  template <typename BT, unsigned int FB>
682  PLAYRHO_CONSTEXPR inline Fixed<BT, FB> GetInvalid() noexcept
683  {
684  return Fixed<BT, FB>::GetNaN();
685  }
686 #endif // CONFLICT_WITH_GETINVALID
687 
689  template <typename BT, unsigned int FB>
690  inline ::std::ostream& operator<<(::std::ostream& os, const Fixed<BT, FB>& value)
691  {
692  return os << static_cast<double>(value);
693  }
694 
710 
711  // Fixed32 free functions.
712 
714  template <>
715  PLAYRHO_CONSTEXPR inline Fixed32 GetInvalid() noexcept
716  {
717  return Fixed32::GetNaN();
718  }
719 
722  {
723  lhs += rhs;
724  return lhs;
725  }
726 
729  {
730  lhs -= rhs;
731  return lhs;
732  }
733 
736  {
737  lhs *= rhs;
738  return lhs;
739  }
740 
743  {
744  lhs /= rhs;
745  return lhs;
746  }
747 
750  {
751  lhs %= rhs;
752  return lhs;
753  }
754 
756  PLAYRHO_CONSTEXPR inline bool operator== (Fixed32 lhs, Fixed32 rhs) noexcept
757  {
758  return lhs.Compare(rhs) == Fixed32::CmpResult::Equal;
759  }
760 
762  PLAYRHO_CONSTEXPR inline bool operator!= (Fixed32 lhs, Fixed32 rhs) noexcept
763  {
764  return lhs.Compare(rhs) != Fixed32::CmpResult::Equal;
765  }
766 
768  PLAYRHO_CONSTEXPR inline bool operator <= (Fixed32 lhs, Fixed32 rhs) noexcept
769  {
770  const auto result = lhs.Compare(rhs);
771  return (result == Fixed32::CmpResult::LessThan) || (result == Fixed32::CmpResult::Equal);
772  }
773 
775  PLAYRHO_CONSTEXPR inline bool operator >= (Fixed32 lhs, Fixed32 rhs) noexcept
776  {
777  const auto result = lhs.Compare(rhs);
778  return (result == Fixed32::CmpResult::GreaterThan) || (result == Fixed32::CmpResult::Equal);
779  }
780 
782  PLAYRHO_CONSTEXPR inline bool operator < (Fixed32 lhs, Fixed32 rhs) noexcept
783  {
784  const auto result = lhs.Compare(rhs);
785  return result == Fixed32::CmpResult::LessThan;
786  }
787 
789  PLAYRHO_CONSTEXPR inline bool operator > (Fixed32 lhs, Fixed32 rhs) noexcept
790  {
791  const auto result = lhs.Compare(rhs);
792  return result == Fixed32::CmpResult::GreaterThan;
793  }
794 
799  template <>
800  inline const char* GetTypeName<Fixed32>() noexcept
801  {
802  return "Fixed32";
803  }
804 
805 #ifdef PLAYRHO_INT128
806  // Fixed64 free functions.
807 
820  using Fixed64 = Fixed<std::int64_t,24>;
821 
823  template <>
824  PLAYRHO_CONSTEXPR inline Fixed64 GetInvalid() noexcept
825  {
826  return Fixed64::GetNaN();
827  }
828 
830  PLAYRHO_CONSTEXPR inline Fixed64 operator+ (Fixed64 lhs, Fixed64 rhs) noexcept
831  {
832  lhs += rhs;
833  return lhs;
834  }
835 
837  PLAYRHO_CONSTEXPR inline Fixed64 operator- (Fixed64 lhs, Fixed64 rhs) noexcept
838  {
839  lhs -= rhs;
840  return lhs;
841  }
842 
844  PLAYRHO_CONSTEXPR inline Fixed64 operator* (Fixed64 lhs, Fixed64 rhs) noexcept
845  {
846  lhs *= rhs;
847  return lhs;
848  }
849 
851  PLAYRHO_CONSTEXPR inline Fixed64 operator/ (Fixed64 lhs, Fixed64 rhs) noexcept
852  {
853  lhs /= rhs;
854  return lhs;
855  }
856 
857  PLAYRHO_CONSTEXPR inline Fixed64 operator% (Fixed64 lhs, Fixed64 rhs) noexcept
858  {
859  lhs %= rhs;
860  return lhs;
861  }
862 
864  PLAYRHO_CONSTEXPR inline bool operator== (Fixed64 lhs, Fixed64 rhs) noexcept
865  {
866  return lhs.Compare(rhs) == Fixed64::CmpResult::Equal;
867  }
868 
870  PLAYRHO_CONSTEXPR inline bool operator!= (Fixed64 lhs, Fixed64 rhs) noexcept
871  {
872  return lhs.Compare(rhs) != Fixed64::CmpResult::Equal;
873  }
874 
875  PLAYRHO_CONSTEXPR inline bool operator <= (Fixed64 lhs, Fixed64 rhs) noexcept
876  {
877  const auto result = lhs.Compare(rhs);
878  return (result == Fixed64::CmpResult::LessThan) || (result == Fixed64::CmpResult::Equal);
879  }
880 
881  PLAYRHO_CONSTEXPR inline bool operator >= (Fixed64 lhs, Fixed64 rhs) noexcept
882  {
883  const auto result = lhs.Compare(rhs);
884  return (result == Fixed64::CmpResult::GreaterThan) || (result == Fixed64::CmpResult::Equal);
885  }
886 
887  PLAYRHO_CONSTEXPR inline bool operator < (Fixed64 lhs, Fixed64 rhs) noexcept
888  {
889  const auto result = lhs.Compare(rhs);
890  return result == Fixed64::CmpResult::LessThan;
891  }
892 
893  PLAYRHO_CONSTEXPR inline bool operator > (Fixed64 lhs, Fixed64 rhs) noexcept
894  {
895  const auto result = lhs.Compare(rhs);
896  return result == Fixed64::CmpResult::GreaterThan;
897  }
898 
900  template<> struct Wider<Fixed32> {
901  using type = Fixed64;
902  };
903 
908  template <>
909  inline const char* GetTypeName<Fixed64>() noexcept
910  {
911  return "Fixed64";
912  }
913 
914 #endif /* PLAYRHO_INT128 */
915 
916 } // namespace playrho
917 
918 #endif // PLAYRHO_COMMON_FIXED_HPP