GP32 Fixed Point Math Trouble


synkro

0xdeadbeef
Joined
Aug 26, 2003
Messages
823
Location
Germany
Website
Visit site
Code:
#ifndef __fixedmath_h__
#define __fixedmath_h__

typedef long fixed;  	// 16.16 fixed point
typedef unsigned short Iangle; // Integer angle (0..1023)

#define MAX_TRIG   1024
#define MAGIC_TRIG 162.974 // degree precision for LUTs

// trigo LUTs
fixed SinTab[MAX_TRIG];
fixed CosTab[MAX_TRIG];

// fixed conversion
#define INT2FIXED(x) ((x) << 16)
#define FIXED2INT(f) ((f) >> 16)
//#define FIXED2FLOAT(f) (((double) (f)) * 1.52587890625e-5)
#define FIXED2FLOAT(f) ((float) ((f) / 65536.0))
#define FLOAT2FIXED(f) ((fixed) ((f) * 65536.0))
#define DOUBLE2FIXED(x) ((x) * 65536.0)

// fixed constants
#define FIXED_ZERO     (INT2FIXED(0))
#define FIXED_ONE      (INT2FIXED(1))
#define FIXED_ONE_HALF (FIXED_HALF(FIXED_ONE))
#define FIXED_PI        205887L
#define FIXED_2PI       411775L
#define FIXED_HALF_PI  (FLOAT2FIXED(1.57079633))
#define FIXED_E         178144L
#define FIXED_ROOT2      74804L
#define FIXED_ROOT3     113512L

// fixed functions
#define FIXED_DIV(a,b) ((fixed) (((double) (a) / (double) (b)) * 65536.0))
#define FIXED_MUL(a,b) ((fixed) ((double) (a) * ((double) (b)) * 1.52587890625e-5))
#define FIXED_ABS(f) ((f) < 0 ? -(f) : (f))
#define FIXED_TRUNC(f) ((f) & 0xffff0000)
#define FIXED_SIGN(f) ((unsigned int) (f) >> 31)
#define FIXED_PRODUCT_SIGN(f, g) ((unsigned int) ((f) ^ (g)) >> 31)
#define FIXED_HALF(f) ((f) / 2)
#define FIXED_DOUBLE(f) ((f) << 1)

#define FIXED_ROUND2INT(f) (((f) & 0x0000FFFF) > 32768 ? ((f) >> 16) + 1 : (f) >> 16)
// output
#define Printfx(x) sprintf(string, "%ld.%ld", x >> 16, 10000 * (unsigned long) ((x) & 0xFFFF) >> 16)
#define NDPrintfx(x) sprintf(string, "%ld", x >> 16)

#endif

I have trouble with my fixed point math (16.16). It seems
that I have a heavy lost in precision on some operations.
So the error must be in the float<->fixed conversion or in
the MUL and DIV. I give you an example, because sometimes
it works perfect and sometimes not....
Code:
fixed fx1 = FLOAT2FIXED(-20.333);
fixed fx2 = INT2FIXED(100);
fixed fx3 = FIXED_MUL(fx1,fx2);

Printfx(fx3);
gp_drawString (1, 1, 10, string, GP32RGB(30,30,30), framebuffer[nflip]);
-> the output is perfect (2033.2)

Buit when I enter:
Code:
fixed fx1 = FLOAT2FIXED(-20.333);
fixed fx2 = INT2FIXED(100);
fixed fx3 = FIXED_MUL(fx1,fx2);
fx1 = -21.66 (how can this happen?)
fx3 = -2034.7007 (where do I loose that much?)

but even the DIV sometimes works perfect, sometimes
not...
Code:
fixed fx1 = FLOAT2FIXED(270.5);
fixed fx2 = INT2FIXED(20);
fixed fx3 = FIXED_DIV(fx1,fx2);
-> perfect fx3 = 13.5249

but when I try to calculate 1/100
Code:
fixed fx1 = INT2FIXED(1);
fixed fx2 = INT2FIXED(100);
fixed fx3 = FIXED_DIV(fx1,fx2);
-> fx = 0.99 (wtf?!)
Help! I must doing something wrong...
 
your fixed to float does not work for negative numbers - I had the same problem myself - the fixed point number in themselves are probably correct. The fix to this is a fudge that is so bad that I am embaressed to show but here goes:

Code:
#define FPOINT16_SetZero(x)    	x.nWhole = 0
#define FPOINT16_Multiply(x,y,Result)  Result.nWhole = ((int)( ( ( (INT64)x.nWhole) * ( (INT64)y.nWhole) ) >> 16 ))
#define FPOINT16_Divide(x,y,Result)  	Result.nWhole = ((int)( ((INT64)(x.nWhole)<<16) / (y.nWhole) ))
#define FPOINT16_FromFloat(x,y)    x.nWhole = (int)(y * 65536.0f)
#define FPOINT16_Decimal(x)    	x.fSplit.decimal
#define FPOINT16_Number(x)    	x.fSplit.number
#define FPOINT16_Whole(x)    	x.nWhole
#define FPOINT16_ToFloat(x,y)    y = (float)x.fSplit.number; \
          	if ((x.nWhole < 0) && (x.fSplit.decimal != 0)) \
          	{ \
            y += (1.0f - (float)((float)(FPOINT_float_to_fixInt16.nWhole - x.fSplit.decimal) / (float)(FPOINT_float_to_fixInt16.nWhole))); \
          	} \
          	else \
          	{ \
            y += ((float)x.fSplit.decimal / (float)(FPOINT_float_to_fixInt16.nWhole)); \
          	}


typedef struct FIXED16_TAG
{
	unsigned int decimal	: 16;
	int number    : 16;
} FIXED16;

typedef union FPOINT16_TAG
{
	FIXED16 fSplit;
	int nWhole;
} FPOINT16;

I also have to call the following initialisation function before I can use my fixed point:

Code:
FPOINT16 FPOINT_float_to_fixInt16; // this is a global variable :-(

int FPOINT_Init()
{
	FPOINT16_SetZero(FPOINT_float_to_fixInt16);
	FPOINT16_Number(FPOINT_float_to_fixInt16) = 1;

	return 0;
}

As you can see, my fixed to float define is slow and I only use it for testing purposes, but it does work.
 
I don't know if this can be, but you can try it anyway. It's a small change, so you can test it fast.
Code:
#define FIXED2FLOAT(f) (((float) (f)) / 65536.0f)
/* Notice the casting of f BEFORE applying the division. */

#define FIXED_HALF(f) ((f) >> 1)
/* Maybe it's faster than your division by two. */
 
atm I am using a union similar to charge's code, the problem are numbers with zeros after the point, also I still have rounding issues by mult nad div :(
Code:
#define INT2FIXED(x) ((x) << 16)
#define FIXED2INT(f) ((f) >> 16)
#define ROUND_FIXED_TO_INT(x)   (((x) + 0x8000) >> 16)

#define Printfx(x) sprintf(string, "%ld.%ld", x >> 16, 100000 * (unsigned long) ((x) & 0xFFFF) >> 16)
#define NDPrintfx(x) sprintf(string, "%ld", x >> 16)


typedef union FIXED
{
    long full;
    struct part
    {
      unsigned int fraction:16;
      signed int integer:16;
    } part;
} tFIXED;



long DOUBLE2FIXED(double f)
{
  tFIXED result;
  if(f<0)
  {
    f *= -1;
    result.full = ((long) ((f) * 65536.0));
    result.part.integer *= -1;
  }
  else
  {
    result.full = ((long) ((f) * 65536.0));
  }

  return result.full;
}

double FIXED2DOUBLE(tFIXED fx)
{
  double result;
  if(fx.part.integer<0)
  {
    fx.part.integer *= -1;
    result = ((double) (fx.full / 65536.0));
    result *= -1;
  }
  else
  {
    result = ((double) (fx.full / 65536.0));
  }

  return result;
}


long MULTFIXED(tFIXED a, tFIXED b)
{
    long temp,result;
    int sign = 1;

    if(a.part.integer < 0)
    {
        sign *= -1;
        a.part.integer *= -1;
    }
    if(b.part.integer < 0)
    {
        sign  *= -1;
        b.part.integer *= -1;
    }

    result = (((a.full & 0x0000FFFF) * (b.full & 0x0000FFFF)))>>16;
    result = result + ((((a.full>>16) * (b.full & 0x0000FFFF))));
    result = result + ((((b.full>>16) * (a.full & 0x0000FFFF))));

    temp = (a.full>>16) * (b.full>>16);
    result = result + (temp<<16);
    return (result *= sign);
}
 
Back
Top