free fixed point math lib for C++?

Started by
4 comments, last by doynax 18 years, 7 months ago
Hello everyone.. Does anyone know of a fast, yet easy and free fixedpoint math library for C++? I would need (want perhaps, can probably add this on my own though) conversions to/from floats, doubles, ints and uints.. And operator overloading.. so, anyone? If noone knows of a free such library, does anyone want to team up with me and write one?
Advertisement
Uhm, why do you think you need fixed-point math? In 99%, floats will be faster, easier to use, convert etc. But as you asked, I'll give you answer. There's Allegro library, which contains such functions for fixed point maths for C, but they should be compatible also with C++. Just download the library, and look at the sources.

As for converting functions (and others), I've written (and partially borrowed from other people ;-) ) those, use them if you want:

(header)

 //! Returns _value converted from degrees <0...360> to radians. float DegToRad(float _value); //! Returns _value converted from radians <-2PI, 2PI> to degrees. float RadToDeg(float _value);   // -------------------------------------------------   /* They all _round_ to nearest integer, not throw away values after decimal point (like normal casting do). ie.    0.1 -> 0    0.2 -> 0    0.3 -> 0    0.4 -> 0    0.5 -> 1    0.6 -> 1    0.7 -> 1    0.8 -> 1    0.9 -> 1    1.0 -> 1*/ ulint FloatToIntPositive(float _f);        //!< 5x times faster than casting slint FloatToIntNegative(float _f);        //!< 5x times faster than casting slint FloatToInt(float _f);                //!< 2x times faster than casting// ------------------------------------------------- LOGARITHMS & POWER OF TWO //! \return Number that is greater then log2 of [_n]. ulint Log2l( ulint _n); //! \return Number that is smaller then log2 of [_n]. ulint Log2g( ulint _n); //! \returns True if _n could be produced by raising 2 to some power. bool IsPowerOfTwo( ulint _n); //! \return Nearest power of two that is greater than _input. usint PowerOfTwo(usint _input);// ------------------------------------------------- CLAMPING //! \return Value of _f clamped to 0, if it is < 0, or _f otherwise. float ClampTo0(float _f); //! \return Value of _f clamped to 1, if it is > 1, or _f otherwise. float ClampTo1(float _f);  //! Clamps both to 0 and 1. float Clamp(float _f); //! General purpose clamping function for all sorts of types and values. template <class T> T ClampTo(T _val, T _minVal, T _maxVal)  {   return Min(Max(_val, _minVal), _maxVal);  }// ------------------------------------------------- TRIGONOMETRY/*! Before first requests to Sin() and Cos() you must call this function. If you don't call InitLib() then you have to do it manually, otherwise they will return junk. */ void InitSinTable(); // -------------------------------------------------   //! \param _theta Should be in radians. //! \return Sinus of _theta. float Sin(float _theta);   //! \param _theta Should be in radians. //! \return Cosinus of _theta. float Cos(float _theta); // ------------------------------------------------- OTHER THINGS  /*!   \return Square root of _x. It's very fast, but on larger numbers (ie. 256*256) gets quite innacurate.   \param If _x <= 0 returns 0.  */  float Sqrt (float _x);


(implementation)

// ------------------------------------------------- CONVERSION// ------------------------------------------------- start RadToDegfloat SC :: RadToDeg(float _radians) {  return (_radians * Radian); }// ------------------------------------------------- end RadToDeg// ------------------------------------------------- start DegToRadfloat SC :: DegToRad(float _degrees) {  return (_degrees * PI / 180.0f);   }  // ------------------------------------------------- end DegToRad// -------------------------------------------------// union for internal use of math librarytypedef union  {    int     i;          // as integer    float   f;          // as float  } INTORFLOAT;// ------------------------------------------------- // ------------------------------------------------- start FloatAbsfloat SC :: Abs(float _f) {  INTORFLOAT ftmp;  ftmp.f = _f;  ftmp.i &= 0x7fffffff;  return ftmp.f; }// ------------------------------------------------- end FloatAbs// ------------------------------------------------- start Sqrtfloat SC :: Sqrt (float _x) {  float _xhalf = 0.5f*_x;  int i = *(int*)&_x;  i = 0x5f3759df - (i >> 1);  _x = *(float*)&i;  _x = _x*(1.5f - _xhalf*_x*_x);  return (1/_x); }// ------------------------------------------------- end Sqrt// ------------------------------------------------- start Log2lSC :: ulint SC :: Log2l ( ulint _n ) {	ulint t, log2;	if ( _n >= 0x10000 )	{		log2 = 16;		t = 0x1000000;	}	else	{		log2 = 0;		t = 0x100;	}	if ( _n >= t )	{		log2 += 8;		t <<= 4;	}	else	{		t >>= 4;	}	if ( _n >= t )	{		log2 += 4;		t <<= 2;	}	else	{		t >>= 2;	}	if ( _n >= t )	{		log2 += 2;		t <<= 1;	}	else	{		t >>= 1;	}	if ( _n >= t )	{		log2 += 1;	}	return log2; }// ------------------------------------------------- end Log2l// ------------------------------------------------- start Log2gSC :: ulint SC :: Log2g( ulint _n ) {	if ( _n > 0x80000000 )		return 32;	ulint t, log2;	if ( _n > 0x8000 )	{		log2 = 16;		t = 0x800000;	}	else	{		log2 = 0;		t = 0x80;	}	if ( _n > t )	{		log2 += 8;		t <<= 4;	}	else	{		t >>= 4;	}	if ( _n > t )	{		log2 += 4;		t <<= 2;	}	else	{		t >>= 2;	}	if ( _n > t )	{		log2 += 2;		t <<= 1;	}	else	{		t >>= 1;	}	if ( _n > t )	{		log2 += 1;	}	return log2; }// ------------------------------------------------- end Log2g// ------------------------------------------------- start PowerOfTwoSC :: usint SC :: PowerOfTwo(usint _input){ usint value = 2; while ( value < _input )   {	value <<= 1;   } return value;}// ------------------------------------------------- end PowerOfTwo// ------------------------------------------------- start IsPowerOfTwobool SC :: IsPowerOfTwo( ulint _n) {  return (Log2g(_n) == Log2l(_n)); }  /*  bool isPowerOfTwo(int v){ return v & (v-1) == 0;}*/ // ------------------------------------------------- end IsPowerOfTwo// ------------------------------------------------- used by some math functionsINTORFLOAT ftoibiasp = {((23 + 127) << 23)};INTORFLOAT ftoibiasn = {((23 + 127) << 23) + (1 << 22)};INTORFLOAT ftmp; // ------------------------------------------------- // ------------------------------------------------- start FloatToIntPositiveSC :: ulint SC :: FloatToIntPositive(float _f) {      ftmp.f = _f;  ftmp.f += ftoibiasp.f;  ftmp.i -= ftoibiasp.i;  return ftmp.i;   } // ------------------------------------------------- end FloatToIntPositive// ------------------------------------------------- start FloatToIntNegative SC :: slint SC :: FloatToIntNegative(float _f) {     ftmp.f = _f;  ftmp.f += ftoibiasn.f;  ftmp.i -= ftoibiasn.i;  return ftmp.i;   } // ------------------------------------------------- end FloatToIntNegative// ------------------------------------------------- start FloatToInt SC :: slint SC :: FloatToInt(float _f) {     return ( (_f < 0) ? (FloatToIntNegative(_f)) : (FloatToIntPositive(_f)) ); } // ------------------------------------------------- end FloatToInt// ------------------------------------------------- start ClampTo0float SC :: ClampTo0(float _f) {  int s = (*(int *)&_f) >> 31;  s = ~s;  *(int *)&_f &= s;  return _f; } // ------------------------------------------------- end ClampTo0// ------------------------------------------------- start ClampTo1 inline float SC :: ClampTo1(float _f){ if (_f > 1.0f)  return 1.0f; return _f;}// ------------------------------------------------- end ClampTo1// ------------------------------------------------- start Clampfloat SC :: Clamp(float _f) {  if (_f > 1.0f) return 1.0f;  else return ClampTo0(_f); }// ------------------------------------------------- end Clamp// -------------------------------------------------// ------------------------------------------------- SinTable preparation start// Sin and Cos both use precalculated sintables that are created by std sin function#undef log#include <cmath>#define SINTABLESIZE 256#define TWOPISCALE ((float)SINTABLESIZE / (SC::PI2)) INTORFLOAT  sinbias = {((23 + 127) << 23) + (1 << 22)};INTORFLOAT  cosbias = {((23 + 127) << 23) + (1 << 22)+ 64}; static float SinTable[SINTABLESIZE];INTORFLOAT  fi;// ------------------------------------------------- void SC :: InitSinTable(void){  int i;  double theta;  // Integer range 0 to SINTABLESIZE is converted to floating  //   point 0 to 2 pi range (radians for a full circle).   for (i = 0; i < SINTABLESIZE; ++i)   {    theta = (double)i/TWOPISCALE;    SinTable = (float)sin(theta);   }}// ------------------------------------------------- SinTable preparation end// ------------------------------------------------- start Sinfloat SC :: Sin(float _theta) {      // Scale to table index range and add conversion bias.  fi.f = _theta * TWOPISCALE + sinbias.f;   // Mask off lower bits, assuming SINTABLESIZE is power of 2.   return SinTable[fi.i & (SINTABLESIZE - 1)]; }// ------------------------------------------------- end Sin// ------------------------------------------------- start Cosfloat SC :: Cos(float _theta) {        fi.f = _theta * TWOPISCALE + cosbias.f;     return SinTable[fi.i & (SINTABLESIZE - 1)]; } // ------------------------------------------------- end Cos


(constants)

 const float PI = 3.1415926535898f;     //!< PI const float PI2 = 6.2831853071796f;    //!< PI * 2 const float PI3 = 9.4247779607694f;    //!< PI * 3 const float PI4 = 12.5663706143592f;   //!< PI * 4// -------------------------------------------------  const float PIDiv2 = 1.5707963267949f; //!< PI / 2 const float PIDiv3 = 1.0471975511966f; //!< PI / 3 const float PIDiv4 = 0.7853981633974f; //!< PI / 4 const float PIDiv6 = 0.5235987755983f; //!< PI / 6// -------------------------------------------------  const float SqrPI = 9.8696044010894f;  //!< PI * PI const float SqrtPI = 1.7724538509055f; //!< Sqrt(PI)// -------------------------------------------------  const float E = 2.7182818284590f;      //!< e const float SqrE = 7.3890560989307f;   //!< e * e const float SqrtE = 1.6487212707001f;  //!< Sqrt(e)// -------------------------------------------------  const float Sqrt2 = 1.4142135623731f;  //!< Sqrt(2) const float Sqrt3 = 1.7320508075689f;  //!< Sqrt(3) const float Sqrt5 = 2.2360679774998f;  //!< Sqrt(5) const float Sqrt10 = 3.1622776601684f; //!< Sqrt(10)// -------------------------------------------------  const float Ln2 = 0.6931471805599f;    //!< Ln(2) const float Ln10 = 2.3025850929940f;   //!< Ln(10)// -------------------------------------------------  const float G = 9.81f;                 //!< g (gravity acceleration) const float SqrG = 96.2361f;           //!< g * g const float SqrtG = 3.1320919526732f;  //!< Sqrt(g)// -------------------------------------------------  const float Radian = 180.0f / PI;      //!< one radian
As Koshmaar said, the Allegro library has fixed point routines, and also provides a C++ wrapper (with overloaded operators and so on). It si cross-platform, but on x86 platforms it's implemented in asm for a speed boost. On Macs it's rather slow, but if you don't care for this check it out. At least you'll get an idea of how to implement one yourself.

[OpenTK: C# OpenGL 4.4, OpenGL ES 3.0 and OpenAL 1.1. Now with Linux/KMS support!]

Ok, thank you.. It (fixed point math that is) is said to be faster, even on faster machines.. Suppose not then.. I mostly wanted it for consistency, cause I'm guessing it'll be somewhat more accurate/reliable than floats?
Quote:Original post by DvDmanDT
Ok, thank you.. It (fixed point math that is) is said to be faster, even on faster machines.. Suppose not then.. I mostly wanted it for consistency, cause I'm guessing it'll be somewhat more accurate/reliable than floats?
Nope, it'll be no more accurate or reliable than floats, because of the IEEE standard. And I think that floats are faster, since the CPU can offload work to the FPU.
Quote:Original post by Evil Steve
Quote:Original post by DvDmanDT
Ok, thank you.. It (fixed point math that is) is said to be faster, even on faster machines.. Suppose not then.. I mostly wanted it for consistency, cause I'm guessing it'll be somewhat more accurate/reliable than floats?
Nope, it'll be no more accurate or reliable than floats, because of the IEEE standard. And I think that floats are faster, since the CPU can offload work to the FPU.
Well, that's not quite the whole truth.
The IEEE standard in itself is unfortunately not enough to make deterministic floating point calculations portable. There's just too much room for platform specific details (think of the x86's ability to make entire calculations in full 80-bit precision for instance) and compiler optimizations (reordering calculations, reciprocal multiplications, etc..). Disabling optimizations helps somewhat, but that's neither a very attractive option nor enough to make things fully predictable.
And when working on a fixed scale (world coordinates for instance) it'll gain you a fair bit of precision (those bits used to specify the exponent), additionally it's quite nice to have the precision spread out evenly across the entire level.

Floats are generally faster (especially the SIMD stuff) and usually easier to deal with. But fixed point still has it's uses.

This topic is closed to new replies.

Advertisement