Jump to content
  • Advertisement
Sign in to follow this  
DvDmanDT

free fixed point math lib for C++?

This topic is 4826 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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?

Share this post


Link to post
Share on other sites
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 RadToDeg

float SC :: RadToDeg(float _radians)
{
return (_radians * Radian);
}

// ------------------------------------------------- end RadToDeg

// ------------------------------------------------- start DegToRad

float SC :: DegToRad(float _degrees)
{
return (_degrees * PI / 180.0f);
}

// ------------------------------------------------- end DegToRad

// -------------------------------------------------

// union for internal use of math library

typedef union
{
int i; // as integer
float f; // as float

} INTORFLOAT;

// -------------------------------------------------

// ------------------------------------------------- start FloatAbs

float SC :: Abs(float _f)
{
INTORFLOAT ftmp;
ftmp.f = _f;
ftmp.i &= 0x7fffffff;
return ftmp.f;
}

// ------------------------------------------------- end FloatAbs

// ------------------------------------------------- start Sqrt

float 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 Log2l

SC :: 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 Log2g

SC :: 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 PowerOfTwo

SC :: usint SC :: PowerOfTwo(usint _input)
{
usint value = 2;

while ( value < _input )
{
value <<= 1;
}
return value;
}

// ------------------------------------------------- end PowerOfTwo

// ------------------------------------------------- start IsPowerOfTwo

bool SC :: IsPowerOfTwo( ulint _n)
{
return (Log2g(_n) == Log2l(_n));
}

/*

bool isPowerOfTwo(int v){
return v & (v-1) == 0;
}

*/


// ------------------------------------------------- end IsPowerOfTwo

// ------------------------------------------------- used by some math functions

INTORFLOAT ftoibiasp = {((23 + 127) << 23)};
INTORFLOAT ftoibiasn = {((23 + 127) << 23) + (1 << 22)};
INTORFLOAT ftmp;

// -------------------------------------------------

// ------------------------------------------------- start FloatToIntPositive

SC :: 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 ClampTo0

float 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 Clamp

float 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 Sin

float 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 Cos

float 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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!