/* * All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or * its licensors. * * For complete copyright and license terms please see the LICENSE at the root of this * distribution (the "License"). All use of this software is governed by the License, * or, if provided, by the license below or the license accompanying this file. Do not * remove or modify any license notices. This file is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ // Original file Copyright Crytek GMBH or its affiliates, used under license. #ifndef CRYINCLUDE_CRYCOMMON_FIXEDPOINT_H #define CRYINCLUDE_CRYCOMMON_FIXEDPOINT_H #pragma once #include #ifdef fabsf #undef fabsf #endif #ifdef fmodf #undef fmodf #endif #ifdef sqrtf #undef sqrtf #endif #ifdef expf #undef expf #endif #ifdef logf #undef logf #endif #ifdef powf #undef powf #endif #ifdef fmodf #undef fmodf #endif /* This file implements a configurable fixed point number type. If base type is unsigned, fixed point number will be unsigned too and any signed base operations will be not work properly. Only 8..32bit basic types are supported. TODO: -When overflow_type is 64bit, shift operations are implemented in software through in the CRT through _allshl, _allshr, _alldiv, _allmul - extra call is generated and no optimizations applied. -Implement basic trigonometry routines: (arc)sin, (arc)cos, (arc)tan */ /* Here's a Visual Studio Debugger Visualizer - to be put under the [Visualizer] section in your autoexp.dat ;------------------------------------------------------------------------------ ; fixed_t ;------------------------------------------------------------------------------ fixed_t<*,*> { preview ( #([$e.v / (float)fixed_t<$T1,$T2>::fractional_bitcount, f]) ) } */ #ifdef _DEBUG #define FIXED_POINT_CHECK_CONSISTENCY #endif namespace CryFixedPoint { // Rounding Modes supported by fixed_t. enum ERoundingModes { eRM_None = 0, // similar to truncation, but uses >> in multiply (results vary for negative numbers) eRM_Truncate, // truncation rounding (or round towards zero) eRM_Nearest, // classic "round to nearest" (or round away from zero) eRM_Last, // internal usage only }; template struct SelectorRoundingMode { SelectorRoundingMode(){}; }; template struct overflow_type { }; template<> struct overflow_type { typedef short type; }; template<> struct overflow_type { typedef unsigned short type; }; template<> struct overflow_type { typedef int type; }; template<> struct overflow_type { typedef unsigned int type; }; template<> struct overflow_type { typedef int64 type; }; template<> struct overflow_type { typedef uint64 type; }; // --- template struct unsigned_overflow_type { }; template<> struct unsigned_overflow_type { typedef unsigned short type; enum { bit_size = sizeof(type) * 8, }; }; template<> struct unsigned_overflow_type { typedef unsigned short type; enum { bit_size = sizeof(type) * 8, }; }; template<> struct unsigned_overflow_type { typedef unsigned int type; enum { bit_size = sizeof(type) * 8, }; }; template<> struct unsigned_overflow_type { typedef unsigned int type; enum { bit_size = sizeof(type) * 8, }; }; template<> struct unsigned_overflow_type { typedef uint64 type; enum { bit_size = sizeof(type) * 8, }; }; template<> struct unsigned_overflow_type { typedef uint64 type; enum { bit_size = sizeof(type) * 8, }; }; } template struct Selector{}; template<> struct Selector{}; template<> struct Selector{}; #define DEFAULT_ROUNDING_POLICY CryFixedPoint::eRM_Truncate const static CryFixedPoint::SelectorRoundingMode kDefaultRoundingPolicy; template struct fixed_t { typedef BaseType value_type; typedef typename CryFixedPoint::overflow_type::type overflow_type; typedef typename CryFixedPoint::unsigned_overflow_type::type unsigned_overflow_type; enum { value_size = sizeof(value_type), }; enum { bit_size = sizeof(value_type) * 8, }; enum { is_signed = std::numeric_limits::is_signed ? 1 : 0, }; enum { integer_bitcount = IntegerBitCount, }; enum { fractional_bitcount = bit_size - integer_bitcount - is_signed, }; enum { integer_scale = 1 << fractional_bitcount, }; enum { half_unit = integer_scale >> 1, }; typedef Selector IsSignedSelector; inline fixed_t() { } inline fixed_t(const fixed_t& other) : v(other.v) { } template void ConstructorHelper(const fixed_t& other, const Selector&) { v = other.get(); v <<= static_cast(fractional_bitcount) - static_cast(fixed_t::fractional_bitcount); } template void ConstructorHelper(const fixed_t& other, const Selector&) { BaseTypeO r = other.get(); r >>= static_cast(fixed_t::fractional_bitcount - fractional_bitcount); v = r; } template inline explicit fixed_t(const fixed_t& other) { ConstructorHelper(other, Selector(fractional_bitcount) >= static_cast(fixed_t::fractional_bitcount)>()); } inline fixed_t(const int& value) : v(value << fractional_bitcount) { } inline fixed_t(const unsigned int& value) : v(value << fractional_bitcount) { } inline explicit fixed_t(const float& value) : v((value_type)(value * integer_scale + (value >= 0.0f ? 0.5f : -0.5f))) { } inline explicit fixed_t(const double& value) : v((value_type)(value * integer_scale + (value >= 0.0 ? 0.5 : -0.5))) { } inline explicit fixed_t(const bool& value) : v((value_type)(value * integer_scale)) { } inline fixed_t operator-() const { fixed_t r; r.v = -v; return r; } inline fixed_t& operator+=(const fixed_t& other) { v += other.v; return *this; } inline fixed_t& operator-=(const fixed_t& other) { v -= other.v; return *this; } inline fixed_t& operator*=(const fixed_t& other) { v = rounded_multiplication(v, other.v); return *this; } inline fixed_t& operator/=(const fixed_t& other) { CRY_ASSERT_MESSAGE(other.v != 0, "ERROR: Divide by ZERO!"); v = rounded_division(v, other.v); return *this; } inline fixed_t operator+(const fixed_t& other) const { fixed_t r(*this); r += other; return r; } inline fixed_t operator-(const fixed_t& other) const { fixed_t r(*this); r -= other; return r; } inline fixed_t operator*(const fixed_t& other) const { fixed_t r(*this); r *= other; return r; } inline fixed_t operator/(const fixed_t& other) const { fixed_t r(*this); r /= other; return r; } inline bool operator<(fixed_t other) const { return v < other.v; } inline bool operator<=(fixed_t other) const { return v <= other.v; } inline bool operator>(fixed_t other) const { return v > other.v; } inline bool operator>=(fixed_t other) const { return v >= other.v; } inline bool operator==(fixed_t other) const { return v == other.v; } inline bool operator!=(fixed_t other) const { return v != other.v; } inline fixed_t& operator++() { v += integer_scale; return *this; } inline fixed_t operator++(int) { fixed_t tmp(*this); operator++(); return tmp; } inline fixed_t& operator--() { v -= integer_scale; return *this; } inline fixed_t operator--(int) { fixed_t tmp(*this); operator--(); return tmp; } inline fixed_t& operator=(const fixed_t& other) { v = other.v; return *this; } inline fixed_t& operator=(const float& other) { fixed_t(other).swap(*this); return *this; } inline fixed_t& operator=(const double& other) { fixed_t(other).swap(*this); return *this; } template inline fixed_t& operator=(const Ty& other) { fixed_t(other).swap(*this); return *this; } inline unsigned_overflow_type sqr() const { return (unsigned_overflow_type(v) * v) >> fractional_bitcount; } inline float as_float() const { return v / (float)integer_scale; } inline double as_double() const { return v / (double)integer_scale; } inline int as_int() const { return v >> fractional_bitcount; } inline unsigned int as_uint() const { return v >> fractional_bitcount; } inline value_type get() const { return v; } inline void set(value_type value) { v = value; } inline void swap(fixed_t& other) { std::swap(v, other.v); } inline static fixed_t max() { fixed_t v; v.set(std::numeric_limits::max()); return v; } inline static fixed_t min() { fixed_t v; v.set(std::numeric_limits::min()); return v; } inline static fixed_t epsilon() { fixed_t v; value_type epsilon = std::numeric_limits::is_integer ? 1 : std::numeric_limits::epsilon(); v.set(epsilon); return v; } inline static fixed_t fraction(int num, int denom) { fixed_t v; v.set((overflow_type(num) << (fractional_bitcount + fractional_bitcount)) / (overflow_type(denom) << fractional_bitcount)); return v; } // Utility functions for the overflow_type used for this template inline static float as_float(const overflow_type& x) { return x / (float)integer_scale; } inline static double as_double(const overflow_type& x) { return x / (double)integer_scale; } inline static int as_int(const overflow_type& x) { return x >> fractional_bitcount; } inline static unsigned int as_uint(const overflow_type& x) { return x >> fractional_bitcount; } inline static fixed_t sqrtf(const unsigned_overflow_type& x) { unsigned_overflow_type root = 0; unsigned_overflow_type remHi = 0; unsigned_overflow_type remLo = x << (fractional_bitcount & 1); unsigned_overflow_type count = (CryFixedPoint::unsigned_overflow_type::bit_size >> 1) + (fractional_bitcount >> 1) - ((fractional_bitcount + 1) & 1); do { remHi = (remHi << 2) | (remLo >> (CryFixedPoint::unsigned_overflow_type::bit_size - 2)); remLo <<= 2; root <<= 1; unsigned_overflow_type div = (root << 1) + 1; if (remHi >= div) { remHi -= div; root += 1; } } while (count--); fixed_t r; r.set(static_cast(root >> (fractional_bitcount & 1))); return r; } AUTO_STRUCT_INFO protected: //////////////////////////////////////////////////// // Multiplication helper functions inline value_type rounded_multiplication(const value_type& a, const value_type& b) { return rounded_multiplication(a, b, kDefaultRoundingPolicy); } inline value_type rounded_multiplication(const value_type& a, const value_type& b, const CryFixedPoint::SelectorRoundingMode&) { const value_type product = static_cast((overflow_type(a) * b) >> fractional_bitcount); #ifdef FIXED_POINT_CHECK_CONSISTENCY fixed_t expectedResult; expectedResult.set(product); CheckIfMultiplicationIsCorrect(a, b, expectedResult, CryFixedPoint::eRM_None); #endif return product; } inline value_type rounded_multiplication(const value_type& a, const value_type& b, const CryFixedPoint::SelectorRoundingMode&) { overflow_type product = overflow_type(a) * b; if (isNegative(product)) { product -= half_unit; product += integer_scale - 1; } else { product += half_unit; } product >>= fractional_bitcount; #ifdef FIXED_POINT_CHECK_CONSISTENCY fixed_t expectedResult; expectedResult.set(static_cast(product)); CheckIfMultiplicationIsCorrect(a, b, expectedResult, CryFixedPoint::eRM_Nearest); #endif return static_cast(product); } inline value_type rounded_multiplication(const value_type& a, const value_type& b, const CryFixedPoint::SelectorRoundingMode&) { overflow_type product = overflow_type(a) * b; if (isNegative(product)) { product += integer_scale - 1; } product >>= fractional_bitcount; #ifdef FIXED_POINT_CHECK_CONSISTENCY fixed_t expectedResult; expectedResult.set(static_cast(product)); CheckIfMultiplicationIsCorrect(a, b, expectedResult, CryFixedPoint::eRM_Truncate); #endif return static_cast(product); } // Division helper functions inline value_type rounded_division(const value_type& a, const value_type& b) { return rounded_division(a, b, kDefaultRoundingPolicy); } inline value_type general_rounded_division(const value_type& a, const value_type& b, const CryFixedPoint::ERoundingModes roundingMode) { CRY_ASSERT_MESSAGE(b != 0, "ERROR: Divide by ZERO!"); overflow_type quotient = (overflow_type(a) << fractional_bitcount) / b; #ifdef FIXED_POINT_CHECK_CONSISTENCY fixed_t expectedResult; expectedResult.set(static_cast(quotient)); CheckIfDivisionIsCorrect(a, b, expectedResult, roundingMode); #endif return static_cast(quotient); } inline value_type rounded_division(const value_type& a, const value_type& b, const CryFixedPoint::SelectorRoundingMode&) { return general_rounded_division(a, b, CryFixedPoint::eRM_None); } inline value_type rounded_division(const value_type& a, const value_type& b, const CryFixedPoint::SelectorRoundingMode&) { return general_rounded_division(a, b, CryFixedPoint::eRM_Truncate); } inline value_type rounded_division(const value_type& a, const value_type& b, const CryFixedPoint::SelectorRoundingMode&) { CRY_ASSERT_MESSAGE(b != 0, "ERROR: Divide by ZERO!"); overflow_type dividend = overflow_type(a); overflow_type divisor = overflow_type(b); const int nRoundingShift = 1; const int nShiftBits = fractional_bitcount + nRoundingShift; overflow_type dividend_scaled = dividend << nShiftBits; overflow_type rounding = fpAbs(divisor); if (isNegative(dividend_scaled)) { dividend_scaled -= rounding; } else { dividend_scaled += rounding; } overflow_type quotient = dividend_scaled / (divisor << nRoundingShift); #ifdef FIXED_POINT_CHECK_CONSISTENCY fixed_t expectedResult; expectedResult.set(static_cast(quotient)); CheckIfDivisionIsCorrect(a, b, expectedResult, CryFixedPoint::eRM_Nearest); #endif return static_cast(quotient); } // ------------------------------------------------------------------- inline static bool isNegative(const value_type& result) { return isNegative(result, IsSignedSelector()); } inline static bool isNegative(const value_type& result, const Selector&) { return result < 0; } inline static bool isNegative(const value_type& result, const Selector&) { return false; } inline static bool isNegative(const overflow_type& result) { return isNegative(result, IsSignedSelector()); } inline static bool isNegative(const overflow_type& result, const Selector&) { return result < 0; } inline static bool isNegative(const overflow_type& result, const Selector&) { return false; } // ------------------------------------------------------------------- inline static value_type fpNegate(const value_type& result) { return fpNegate(result, IsSignedSelector()); } inline static value_type fpNegate(const value_type& result, const Selector&) { return -result; } inline static value_type fpNegate(const value_type& result, const Selector&) { return result; } inline static overflow_type fpNegate(const overflow_type& result) { return fpNegate(result, IsSignedSelector()); } inline static overflow_type fpNegate(const overflow_type& result, const Selector&) { return -result; } inline static overflow_type fpNegate(const overflow_type& result, const Selector&) { return result; } //// ------------------------------------------------------------------- inline static value_type fpAbs(const value_type& result) { return fpAbs(result, IsSignedSelector()); } inline static value_type fpAbs(const value_type& result, const Selector&) { return (result >= 0 ? result : -result); } inline static value_type fpAbs(const value_type& result, const Selector&) { return result; } inline static overflow_type fpAbs(const overflow_type& result) { return fpAbs(result, IsSignedSelector()); } inline static overflow_type fpAbs(const overflow_type& result, const Selector&) { return (result >= 0 ? result : -result); } inline static overflow_type fpAbs(const overflow_type& result, const Selector&) { return result; } /////////////////////////////////////////// #ifdef FIXED_POINT_CHECK_CONSISTENCY void CheckIfMultiplicationIsCorrect(const value_type& a, const value_type& b, const fixed_t& expectedResult, const CryFixedPoint::ERoundingModes roundingMode) { // Slow but precise multiplication calculation using only positive numbers const bool a_isNeg = isNegative(a); const bool b_isNeg = isNegative(b); overflow_type a_pos = fpAbs(overflow_type(a)); overflow_type b_pos = fpAbs(overflow_type(b)); overflow_type product2 = a_pos * b_pos; // perform any required rounding if (roundingMode == CryFixedPoint::eRM_Nearest) { product2 += half_unit; } // shift result back into normal scale product2 >>= fractional_bitcount; // if product was supposed to be negative, set sign now. const bool sign = (a_isNeg ^ b_isNeg); if (is_signed && sign) { product2 = fpNegate(product2); } // compare alternative math with actual results fixed_t product_from_alt; product_from_alt.set(static_cast(product2)); CRY_ASSERT_TRACE(expectedResult == product_from_alt, ("ERROR: %.6f / %.6f should equal %.6f, but instead it is %.6f", a / (double)integer_scale, b / (double)integer_scale, product_from_alt.as_double(), expectedResult.as_double())); } void CheckIfDivisionIsCorrect(const value_type& dividend, const value_type& divisor, const fixed_t& expectedResult, const CryFixedPoint::ERoundingModes roundingMode) { // compute quotient using positive numbers const bool dividend_isNeg = isNegative(dividend); const bool divisor_isNeg = isNegative(divisor); overflow_type dividend_pos = fpAbs(dividend); overflow_type divisor_pos = fpAbs(divisor); overflow_type quotient2 = (dividend_pos << fractional_bitcount) / divisor_pos; // perform any required rounding if (roundingMode == CryFixedPoint::eRM_Nearest) { // If twice the remainder is >= the divisor, quotient must be rounded. // This is more accurate than comparing remainder with half the dividend // since it ensures that we will not lose any precision on the right. overflow_type remainder = (dividend_pos << fractional_bitcount) % divisor_pos; if ((remainder << 1) >= divisor_pos) { quotient2 += overflow_type(1); } } // if quotient was supposed to be negative, set sign now. const bool sign = (dividend_isNeg ^ divisor_isNeg); if (is_signed && sign) { quotient2 = fpNegate(quotient2); } // compare alternative math with actual results fixed_t quotient_from_alt; quotient_from_alt.set(static_cast(quotient2)); CRY_ASSERT_TRACE(expectedResult == quotient_from_alt, ("ERROR: %.6f / %.6f should equal %.6f, but instead it is %.6f", dividend / (double)integer_scale, divisor / (double)integer_scale, quotient_from_alt.as_double(), expectedResult.as_double())); } #endif /////////////////////////////////////////// value_type v; }; template inline fixed_t floor(const fixed_t& x) { fixed_t r; r.set(x.get() & ~(fixed_t::fractional_bitcount - 1)); return r; } template inline fixed_t ceil(const fixed_t& x) { fixed_t r; r.set(x.get() & ~(fixed_t::fractional_bitcount - 1)); if (x.get() & (fixed_t::fractional_bitcount - 1)) { r += fixed_t(1); } return r; } // disable fabsf for unsigned base types to avoid sign warning template inline fixed_t fabsf(const fixed_t& x) { typedef fixed_t type; const BaseType mask = x.get() >> (type::bit_size - 1); type r; r.set((x.get() + mask) ^ mask); return r; } template inline fixed_t fmodf(const fixed_t& x, const fixed_t& y) { fixed_t r; r.set(x.get() % y.get()); return r; } template inline fixed_t sqrtf(const fixed_t& x) { typedef fixed_t type; typedef typename CryFixedPoint::overflow_type::type overflow_type; overflow_type root = 0; overflow_type remHi = 0; overflow_type remLo = overflow_type(x.get()) << (type::fractional_bitcount & 1); overflow_type count = (type::bit_size >> 1) + (type::fractional_bitcount >> 1) - ((type::fractional_bitcount + 1) & 1); do { remHi = (remHi << 2) | (remLo >> (type::bit_size - 2)); remLo <<= 2; remLo &= (overflow_type(1) << type::bit_size) - 1; root <<= 1; root &= (overflow_type(1) << type::bit_size) - 1; overflow_type div = (root << 1) + 1; if (remHi >= div) { remHi -= div; remHi &= (overflow_type(1) << type::bit_size) - 1; root += 1; } } while (count--); type r; r.set(static_cast(root >> (type::fractional_bitcount & 1))); return r; } template inline fixed_t isqrtf(const fixed_t& x) { if (x > fixed_t(0)) { fixed_t r(1); r /= sqrtf(x); return r; } return fixed_t(0); } //http://en.wikipedia.org/wiki/Taylor_series template inline fixed_t expf(const fixed_t& x) { const fixed_t one(1); fixed_t w(one); fixed_t y(one); fixed_t n(one); for (; w != 0; ++n) { y += (w *= x / n); } return y; } template inline fixed_t logf(const fixed_t& x) { fixed_t b((x - 1) / (x + 1)); fixed_t w; fixed_t y(b); fixed_t z(b); fixed_t n(3); b *= b; for (; w != 0; n += 2) { z *= b; w = z / n; y += w; } return y + y; } template inline fixed_t powf(const fixed_t& x, const fixed_t& y) { const fixed_t zero(0); if (x < zero) { if (fmodf(y, fixed_t(2)) != zero) { return -expf((y * logf(-x))); } else { return expf((y * logf(-x))); } } else { return expf(y * logf(x)); } } #endif // CRYINCLUDE_CRYCOMMON_FIXEDPOINT_H