gbadev.org forum archive

This is a read-only mirror of the content originally found on forum.gbadev.org (now offline), salvaged from Wayback machine copies. A new forum can be found here.

C/C++ > fixed point macro

#28367 - sgeos - Sat Oct 30, 2004 6:26 am

Is there any reason not to use something like this for constants?
Code:
typedef unsigned long   u32;
typedef u32      FIXED;

/***   TOFIXED(a,b)
 *
 *   a = floating point value
 *   b = fixed point fractional component
 */
#define TOFIXED(a,b)   (  (FIXED)  (((float)a) * (1 << (b)))  )

const FIXED table[] = {TOFIXED(1.5, 8), ...};
x *= TOFIXED(4/3, SOME_RESOLUTION);
x /= ...


-Brendan

#28369 - allenu - Sat Oct 30, 2004 6:37 am

sgeos wrote:
Is there any reason not to use something like this for constants?


I'm not sure what you're asking here. It looks like you are trying to convert a floating point value to a fixed-point (24.8) format using a macro for convenience. If so, your macro looks like it should work and if it's convenient for you, go for it. I don't see any problems with it.

#28383 - poslundc - Sat Oct 30, 2004 3:28 pm

Well, there's one sneaky problem with it, that you actually identify with your example:

((float)a)

... should probably be

((float)(a))

In the example you give:

TOFIXED(4/3, SOME_RESOLUTION);

It will actually generate what you want, because it comes out as:

((float)4/3) == 1.333...

But in, say for example, the following situation:

TOFIXED(4/3+1/2, SOME_RESOLUTION);

It will give you the same answer, because:

((float)4/3+1/2) == 1.333... + 0 == 1.333...

You should enclose the a in its own parentheses, and rely on yourself to cast numbers to float if you want to do manual division, eg.

TOFIXED((float)4/3, SOME_RESOLUTION); or
TOFIXED(4.0/3, SOME_RESOLUTION);

For the record, I like to precalculate my fixed-point constants and load them manually into my program so I can see what they look like, rather than letting the precompiler do the work for me. To each his own.

Dan.

#28488 - Cearn - Mon Nov 01, 2004 9:50 am

On a side note ...
sgeos wrote:

Code:
typedef unsigned long   u32;
typedef u32      FIXED;

Do not use unsigned numbers for fixed points. The fixed point notation is an integer representation of real numbers; these can be both positive or negative, so they should be signed.
Most of the time it won't really matter, but when converting between types or shifting down it will. For example, you'd really want a 16bit "-1" (0xffff) to turn into a 32bit "-1" (0xffffffff), and not "65535" (0x0000ffff), which is what would happen using unsigned shorts.

#28493 - poslundc - Mon Nov 01, 2004 3:04 pm

Ooh, very good point. I've been nailed on that one a couple of times in the past when doing highly mathematical stuff (like my Mode 7 engine).

Dan.

#28495 - ampz - Mon Nov 01, 2004 6:22 pm

That is assuming you are working with signed numbers (which is not allways the case).
For unsigned math there is no reason to use signed numbers, and you gain one bit of precision by using unsigned.

#28608 - Cearn - Wed Nov 03, 2004 10:11 am

ampz wrote:
That is assuming you are working with signed numbers (which is not always the case).
For unsigned math there is no reason to use signed numbers, and you gain one bit of precision by using unsigned.

Fair enough, if you're absolutely sure all your calculations will be positive and you need the extra bit, then by all means use unsigned numbers. But remember that a simple subtraction of unsigned values get give negative results (5 - 8 = -3). From a purely mathematical viewpoint, FIXEDs should be signed; you can always go to unsigned afterwards if you know exactly what you're doing.