Fixed Point Problems...


PokeParadox

Founder of Pirate Games - Penjin Coder
Staff member
Joined
Dec 8, 2005
Messages
6,603
Age
40
Location
UK
Website
pokeparadox.itch.io
WEBSITE
https://github.com/pokeparadox
YOUTUBE
pokeparadox
Ok... Basically I setup some code to use fixed point for my timer. When I forced my windows compile to use this, it behaves like the version with floats but saves some CPU...

On the GP2X and now on my Ubuntu setup when I compile it it goes screwy and never leaves the title screen... Music plays... but it just sits there...

How many bits should I shift for a timer scaler?
 
PokeParadox said:
Ok... Basically I setup some code to use fixed point for my timer. When I forced my windows compile to use this, it behaves like the version with floats but saves some CPU...

On the GP2X and now on my Ubuntu setup when I compile it it goes screwy and never leaves the title screen... Music plays... but it just sits there...

How many bits should I shift for a timer scaler?
Fixed point should always work out the same if you've declared it all clearly enough, and aren't relying on whacky system dependant stuff.

ie:

to be safe, declare things like "unsigned int foo" (or better still, "uint32_t foo" etc), in case your compiler is set to make "int" a 16 or 32 or 64 on one side and different on the other..

If you're using unsigned 32bit ints on all platforms, the fixed point code itself should be fine.. so log it, and look elsewhere :)

jeff
 
Last edited by a moderator:
Thanks! More info as and when it develops... I've put together a Fixed point class, based on what A_SN and Dr Ian have told me about fixed point stuff... it obviously needs to be working correctly to be useful and differing sizes for ints between compilers sounds likely now that you mention it...
 
Just be careful to declare things right, and you'll be cool.

ie: I use uint32_t or UInt32 or other standardized types, typedef'd to the actual types a naive compiler uses.

ie: sometimes 'unsigned int' is 32b (usually), but sometimes its 16 or 64 or the like. So I always use UInt32 (say), and have a header that changes for each system so that I know I'm getting a 32bit value, regardless of the platform.

That could be your problem; likely not, but possibly .. when using other peoples builds of the toolchain, you never know. (ie: I've seen 'char' that menas unsigned, when really char should always be 'signed char' by default. So in my code to be safe I always use 'signed char' or 'unsigned char' in full, just to be safe....)

jeff
 
Ok... I'll post code... but because I use classes it may not be what we are expecting... I've trimmed off includes, inclusion guard statements etc... Also just to mention I use a commentable #define to force my PC build to use the fixed point.

Fixed.h
CODE

class Fixed
{
public:
// Constructors
Fixed(const Fixed &fixNum)
{
fixedNum = fixNum.fixedNum;
scaler = fixNum.scaler;
}
Fixed(const Fixed *fixNum)
{
fixedNum = fixNum->fixedNum;
scaler = fixNum->fixedNum;
}
Fixed() // Creates a default precision Fixed number
{
fixedNum = 0;
scaler = 8;
}
Fixed(int scaler) // Creates a fixed number with the set precision
{
// Scaler is the number of bits for the decimal part
this->scaler = scaler;
fixedNum = 0;
}
~Fixed(){};

void setScaler(int scaler)
{
this->scaler = scaler;
}

//const Fixed& operator= ( const Fixed& fixVal);
//const Fixed& operator= ( const float& val);
// DIVISION
Fixed div(Fixed b);
//Fixed operator%(Fixed b);
Fixed operator/(Fixed b);
// MULTIPLICATION
Fixed mul(Fixed b);
Fixed operator*(Fixed b);
// ADDITION
Fixed add(Fixed b);
Fixed operator+(Fixed b);
// SUBTRACTION
Fixed sub(Fixed b);
Fixed operator-(Fixed b);

//CONSOLE INPUT/OUPUT

void assign(int val)
{
intToFixed(val);
}
void assign(float val)
{
floatToFixed(val);
}
void assign(Fixed val)
{
fixedNum = val.getValue();
scaler = val.getScaler();
}

uint32_t getValue()
{
return fixedNum;
}
int getScaler()
{
return scaler;
}
void floatToFixed(float val);
void intToFixed(int val);
float fixedToFloat();
int fixedToInt();
uint32_t unsign(uint32_t* a)
{
if (*a >= 0)
return 0;
*a = -*a;
return 1;
}
private:
uint32_t fixedNum;// Stores our fixed point number
int scaler; // The precision of the fixed num
};



Fixed.cpp
CODE

// Type conversion
void Fixed::intToFixed(int val)
{
val <<= scaler;
fixedNum = val;
}

int Fixed::fixedToInt()
{
return fixedNum >>= scaler;
}

void Fixed::floatToFixed(float val)
{
uint32_t temp = 1;
temp <<= scaler;
fixedNum = (uint32_t)(val * temp);
}

float Fixed::fixedToFloat()
{
uint32_t temp = 1;
temp <<= scaler;

return ((float)fixedNum/(float)temp);
}
// MULTIPLICATION
Fixed Fixed::mul(Fixed b)
{
uint32_t af=fixedNum&0xFF, bf=b.fixedNum&0xFF;

fixedNum >>= scaler;
b.fixedNum >>= scaler;
Fixed output(scaler);
output.fixedNum = (fixedNum*bf + b.fixedNum*af + (af*bf>>scaler));
output.fixedNum += fixedNum*b.fixedNum<<scaler;
return output;
}

Fixed Fixed::eek:perator *(Fixed b)
{
return mul(b);
}

// DIVISION
Fixed Fixed::div(Fixed b)
{
Fixed r(scaler);
int s = scaler;
int as = unsign(&fixedNum), bs = unsign(&b.fixedNum), rs = as ^ bs; //needed for sign support only

//if (b == 0) return 0;

while (b.fixedNum < fixedNum)
{
b.fixedNum <<= 1;
s++;
}
int hexScaler = 0x40 << (32-scaler);
while (!(b.fixedNum & hexScaler))
{
fixedNum <<= 1;
b.fixedNum <<= 1;
}


while (fixedNum!=0 && s>=0)
{
if (fixedNum >= b.fixedNum)
{
fixedNum -= b.fixedNum;
r.fixedNum += (1 << s);
}
else
{
b.fixedNum >>= 1;
s--;
}
}

if (rs!=0)
r.fixedNum = -r.fixedNum;

return r;
}


Fixed Fixed::eek:perator/(Fixed b)
{
return div(b);
}

// ADDITION
Fixed Fixed::add(Fixed b)
{
Fixed output(scaler);
output.fixedNum = fixedNum + b.fixedNum;
return output;
}

Fixed Fixed::eek:perator+(Fixed b)
{
return add(b);
}

// SUBTRACTION
Fixed Fixed::sub(Fixed b)
{
Fixed output(scaler);
output.fixedNum = fixedNum - b.fixedNum;
return output;
}

Fixed Fixed::eek:perator-(Fixed b)
{
return sub(b);
}



Timer.h
CODE

class Timer
{
public:
Timer()
{
time = 0;
stored = 0;
lastUpdate = SDL_GetTicks();
mode = SIXTY_FRAMES;
#ifdef PLATFORM_PC
scaler = 0.0f;
#endif
#ifdef PLATFORM_GP2X
scaler.setScaler(8);
scaler.floatToFixed(0.0f);
#endif
}
Timer(int lim)
{
time = lim;
stored = 0;
lastUpdate = SDL_GetTicks();
mode = SIXTY_FRAMES;
#ifdef PLATFORM_PC
scaler = 0.0f;
#endif
#ifdef PLATFORM_GP2X
scaler.setScaler(8);
scaler.floatToFixed(0.0f);
#endif
}
~Timer(){};

void setLimit(int timeLimit) // Set countdown limit
{
time = timeLimit;
}
void setMode(uint mode) // Set the timer scaler
{
this->mode = mode;
calcScaler();
}
void incLimit(int changeOfLimit)// Change the current timelimit
{
time += changeOfLimit;
}
void storeTime() // Store the current time
{
stored = time;
}
int getDifference() // Return the difference between stored time and current time
{
return stored - time;
}
int getRemaining() // Return the remaining coutdown time
{
return time;
}
void update() // progress the timer.
{
#ifdef PLATFORM_GP2X
Fixed temp(8);
temp.assign((int)(SDL_GetTicks() - lastUpdate));
if(temp.getValue() > scaler.getValue()) //one unit has passed
#endif
#ifdef PLATFORM_PC
if(SDL_GetTicks() - lastUpdate > scaler) //one unit has passed
#endif
{
lastUpdate = SDL_GetTicks();
time--;
//cout << "Timer: " << time << endl;
}
}

private:
void calcScaler(); // Calculate the resolution of timer updates
uint mode;
int time;
int stored;
int lastUpdate;
#ifdef PLATFORM_GP2X
Fixed scaler;
#endif
#ifdef PLATFORM_PC
float scaler;
#endif
};



Timer.cpp
CODE

void Timer::calcScaler()
{
#ifdef PLATFORM_PC
scaler = 0.0f;
if (mode == FIFTEEN_FRAMES)
{
scaler = (float)CLOCKS_PER_SEC / 15.0f;
}
else if (mode == THIRTY_FRAMES)
{
scaler = (float)CLOCKS_PER_SEC / 30.0f;
}
else if (mode == FIFTY_FRAMES)
{
scaler = (float)CLOCKS_PER_SEC / 50.0f;
}
else if (mode == SIXTY_FRAMES)
{
scaler = (float)CLOCKS_PER_SEC / 60.0f;
}
else if (mode == NANO_SECONDS)
{
scaler = CLOCKS_PER_SEC / 1000000000;
}
else if (mode == MICRO_SECONDS)
{
scaler = CLOCKS_PER_SEC / 1000000;
}
else if (mode == MILLI_SECONDS)
{
scaler = CLOCKS_PER_SEC / 1000;
}
else if (mode == CENTI_SECONDS)
{
scaler = CLOCKS_PER_SEC / 100;
}
else if (mode == DECI_SECONDS)
{
scaler = CLOCKS_PER_SEC / 10;
}
else if (mode == SECONDS)
{
scaler = CLOCKS_PER_SEC;
}
else if (mode == MINUTES)
{
scaler = CLOCKS_PER_SEC * 60;
}
else if (mode == HOURS)
{
scaler = CLOCKS_PER_SEC * 60 * 60;
}
#endif

#ifdef PLATFORM_GP2X
scaler.assign(0.0f);
if (mode == FIFTEEN_FRAMES)
{
scaler.floatToFixed((float)CLOCKS_PER_SEC / 15.0f);
}
else if (mode == THIRTY_FRAMES)
{
scaler.floatToFixed((float)CLOCKS_PER_SEC / 30.0f);
}
else if (mode == FIFTY_FRAMES)
{
scaler.floatToFixed((float)CLOCKS_PER_SEC / 50.0f);
}
else if (mode == SIXTY_FRAMES)
{
scaler.floatToFixed((float)CLOCKS_PER_SEC / 60.0f);
}
else if (mode == NANO_SECONDS)
{
scaler.floatToFixed(CLOCKS_PER_SEC / 1000000000);
}
else if (mode == MICRO_SECONDS)
{
scaler.floatToFixed(CLOCKS_PER_SEC / 1000000);
}
else if (mode == MILLI_SECONDS)
{
scaler.floatToFixed(CLOCKS_PER_SEC / 1000);
}
else if (mode == CENTI_SECONDS)
{
scaler.floatToFixed(CLOCKS_PER_SEC / 100);
}
else if (mode == DECI_SECONDS)
{
scaler.floatToFixed(CLOCKS_PER_SEC / 10);
}
else if (mode == SECONDS)
{
scaler.floatToFixed(CLOCKS_PER_SEC);
}
else if (mode == MINUTES)
{
scaler.floatToFixed(CLOCKS_PER_SEC * 60);
}
else if (mode == HOURS)
{
scaler.floatToFixed(CLOCKS_PER_SEC * 60 * 60);
}
#endif
}



And finally where I actually use the classes:
CODE

bool Engine::stateLoop()
{
// Check state for exit condition
if(state->getNullifyState())
{
state->nullifyState();
return false; // End program execution
}
else if (state->getNeedInit() == false)
{
// Update timer
gameTimer.update();

if(gameTimer.getRemaining() <= 0)
{
// Update physics
state->update();
// Take input
state->keyboard();
// Render objects
state->render(screen);
gameTimer.setLimit(1);
}
else
{
SDL_Delay(1); // Release CPU briefly
}
return true; // Continue program execution
}
else
{
// check and change states
getVariables();
state->clear();
stateManagement();
setVariables();

// Initialise the changed state
state->init();
state->setNeedInit(false); // Set that we have performed the init
return true; // Continue program execution
}
// Should never reach here
return false;
}
 
Back
Top