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.

Beginners > Fixed Point Math with negative numbers

#19126 - alek - Sun Apr 11, 2004 7:35 pm

I get the weired results when I wan't to work with fixed point numbers with a negative sign. I got these defines while I was searching google

Code:


#define itofx(x) ((x) << 8)      // Integer to fixed point
#define ftofx(x) (long)((x) * 256)   // Float to fixed point
#define dtofx(x) (long)((x) * 256)   // Double to fixed point
#define fxtoi(x) ((x) >> 8)      // Fixed point to integer
#define fxtof(x) ((float) (x) / 256)   // Fixed point to float
#define fxtod(x) ((double)(x) / 256)   // Fixed point to double
#define Mulfx(x,y) (((y) * (x)) >> 8)   // Multiply a fixed by a fixed
#define Divfx(x,y) ((y << 8) / (x))       // Divide a fixed by a fixed
#define Printfx(x) printf("%ld.%ld", x >> 8, 100 * (unsigned long) ((x) & 0x00ff) >> 8)                                            // Print fixed point.
#define NDPrintfx(x) printf("%ld", x >> 8)   // Print fixed point out a decimal point.
typedef long fixed;                                   // Our new fixed point type.


When i write
Code:

long test = ftofx(-1.37);
long test2 = Mulfx(test,ftofx(0.234));
Printfx(test2);

It prints -1,68, the answer should be -0,32058
But if I write the same thing without the - sign
Code:

long test = ftofx(1.37);
long test2 = Mulfx(test,ftofx(0.234));
Printfx(test2);

it prints 0.31 wich is much closer to the truth(0,32058).

Another thing, when I write
Code:

long test = ftofx(1.37);
Printfx(test);

it print 1.36... why doesn't it print 1.37?

Thanks in advance...

#19129 - poslundc - Sun Apr 11, 2004 7:53 pm

Well, simply put, the formula being used for printing doesn't work with negative numbers because of the way negative numbers are represented internally (two's-complement). The formula only works for positive numbers. If you want it to work for negative numbers, check to see if the number is less than zero, and if it is, print a negative sign and then call your print macro on the negative of the variable.

For your second question: the macros you are using are creating 24.8 fixed numbers, which means 24 bits of integer precision (up to +/- 8 million or so, 16 million unsigned) and 8 bits of fractional precision. With only 8 bits of fractional precision, 0.36 is as close as you're gonna get to accurate internal representation of your number.

Dan.

#19137 - alek - Sun Apr 11, 2004 9:48 pm

poslundc wrote:
Well, simply put, the formula being used for printing doesn't work with negative numbers because of the way negative numbers are represented internally (two's-complement). The formula only works for positive numbers.


Does that also apply for the other formulas, like Mulfx, Divfx,...?

#19141 - poslundc - Sun Apr 11, 2004 10:43 pm

No, those will all work on signed values, but you still should be careful. These macros are not an insulated wrapper against the numbers' internal representations, and you are very likely to run into overflow and underflow errors if you don't attempt a greater understanding of how fixed-point math works.

Dan.

#19158 - alek - Mon Apr 12, 2004 6:29 pm

poslundc wrote:

For your second question: the macros you are using are creating 24.8 fixed numbers, which means 24 bits of integer precision (up to +/- 8 million or so, 16 million unsigned) and 8 bits of fractional precision. With only 8 bits of fractional precision, 0.36 is as close as you're gonna get to accurate internal representation of your number.


I changed to 16.16 fixed numbers but got the same result. Then I simply added 0.5 (in accordance with Tricks of the 3D game programming gurus) to the number when I converted to fixed point and that did the trick.

poslundc wrote:
you are very likely to run into overflow and underflow errors if you don't attempt a greater understanding of how fixed-point math works.


I've searched the web but I couldn't find any good tutorials on the subject, does anybody know a good tutorial on fixed point math?

#19159 - poslundc - Mon Apr 12, 2004 7:00 pm

http://www.google.com/search?q=fixed+point+math

Dan.

#19160 - alek - Mon Apr 12, 2004 7:20 pm

poslundc wrote:
http://www.google.com/search?q=fixed+point+math

Dan.


Tried that, guess I'll go threw them I little more carefully.

Thanks for the help

#19161 - Lupin - Mon Apr 12, 2004 7:20 pm

you just got to know that if you multiply (1<<16) with (1<<16), the result won't be (1<<16) as you would assume it when multiplying 1.0 with 1.0, the result will be 4294967296 or -1 (i am not quite sure, but it will be a very large number that barely fits into 32 bits).
This is because if you mutliply a number with 16 bit fraction with another number with 16 bit fraction you will get a 0.32 fixed point number (16+16=32).

Because of this most people use 24.8 fixed point math, it won't overflow too soon. If you don't do any mutliplies it is not a problem though.
_________________
Team Pokeme
My blog and PM ASM tutorials

#19162 - poslundc - Mon Apr 12, 2004 8:37 pm

Lupin wrote:
you just got to know that if you multiply (1<<16) with (1<<16), the result won't be (1<<16) as you would assume it when multiplying 1.0 with 1.0, the result will be 4294967296 or -1 (i am not quite sure, but it will be a very large number that barely fits into 32 bits).


;)

You should try it and see. I think you'll be surprised.

Dan.

#19163 - Lupin - Mon Apr 12, 2004 9:33 pm

Yeah, it should be 0. I didn't thought about it because it doesn't matter, multiplying 16.16 by 16.16 will always screw up the result if you forget to shift the numbers :)
_________________
Team Pokeme
My blog and PM ASM tutorials

#19164 - poslundc - Mon Apr 12, 2004 10:26 pm

Lupin wrote:
Yeah, it should be 0. I didn't thought about it because it doesn't matter, multiplying 16.16 by 16.16 will always screw up the result if you forget to shift the numbers :)


No it doesn't, it just gives you a result in 0.32 notation, which is useful in many applications.

Dan.

#19165 - Lupin - Mon Apr 12, 2004 11:18 pm

it screws up the integer part, ok?
_________________
Team Pokeme
My blog and PM ASM tutorials

#19166 - poslundc - Tue Apr 13, 2004 1:01 am

Actually, if you use the long long datatype (or the UMULL or SMULL assembly instructions) you get 64 bits of precision, with 32 bits for your integer portion.

Dan.

#19196 - tepples - Tue Apr 13, 2004 11:32 pm

GCC's ARM backend is highly suboptimal with respect to smull.

I wrote this C code:
Code:
int fixmul(int r0, int r1)
{
  return ((long long)r0 * r1) >> 16;
}

and compiled it on GCC 3.2.3 (DevKit Advance R5b3): using -O3 -marm -mthumb-interwork:
Code:
fixmul:
  str r4, [sp, #-4]!
  smull r3, r4, r0, r1
  mov r1, r4
  mov r0, r3
  mov r2, r3, lsr #16
  orr r0, r2, r1, asl #16
  ldmfd sp!, {r4}
  bx lr

Compare this to handcoded assembly language:
Code:
@ int fixmul(int a, int b)
fixmul:
  smull r1, r2, r0, r1     @ r2:r1 = (long long)r0 * r1
  mov r0, r2, lsl #16      @ r0 = r2 << 16
  orr r0, r0, r1, lsr #16  @ r0 |= (unsigned int)r1 >> 16
  bx lr                    @ return r0

and you'll see why anything involving 16.16 fixed-point will eventually need to be rewritten in ARM assembly language and stashed in IWRAM. The code that GCC emits juggles more registers and even has to hit the stack, making it a total of seven cycles longer. It looks to me like another symptom of GCC's register allocator's trouble with handling destructive sub-operations such as <<= and |=.

Has this improved in more recent GCC? If so, can somebody point me to a HOWTO for getting started with Wintermute's devkitARM, given that DevKit Advance hasn't been updated in over nine months?
_________________
-- Where is he?
-- Who?
-- You know, the human.
-- I think he moved to Tilwick.

#19213 - poslundc - Wed Apr 14, 2004 3:10 am

If the function were inlined, you might be able to avoid the stack hit.

But I agree that it's a lousy implementation overall.

Dan.