#7126 - MumblyJoe - Tue Jun 10, 2003 6:30 am
Is it my imagination or do floating point numbers make the gba grind to a halt? Is this true for anyone else or does some of my other code have issues I cant see? I implimented a collision detection routing which uses doubles, but it virtually destroyed my code's speed. Does the gba even have an FPU?
_________________
www.hungrydeveloper.com
Version 2.0 now up - guaranteed at least 100% more pleasing!
#7136 - Sweex - Tue Jun 10, 2003 11:40 am
GBA doesn't have an FPU, so floating-point operations are done in software which is slow. Using doubles is even (a lot!) worse!
Most people use fixed point variables, although I find them a bit confusing and never really need them anyway.
#7204 - MumblyJoe - Thu Jun 12, 2003 5:48 am
Yeah, I have been playing with the idea, and have made a class with overloaded operators which has a private int, and the class pretends to be a float for all intents and purposes, but only does any floating point calculations when bieng used in the context of a float. Seems pretty fast
_________________
www.hungrydeveloper.com
Version 2.0 now up - guaranteed at least 100% more pleasing!
#7216 - Sweex - Thu Jun 12, 2003 11:54 am
That's not a bad idea at all for using fixed-points!
#7248 - MumblyJoe - Fri Jun 13, 2003 4:06 am
Well here is what i was working on, the only time it takes a float is for construction and the only time it uses a float is to return what it represents through the ToFloat() function, it has overloaded operators so it can perform the most useful arithmatic with other Floaters only (i mean what would be the point if it still multiplied by normal floats.
As you can see, if you pass it a float as a constructor, it is multiplied by 100 and stored in num, so it is basically the same number, but only to two decimal points. When it returns a float in ToFloat it divides num by 100. If it is constructed with an int it expects the user to have taken this into account so it doesnt multiply by 100. So the safest way to construct it is with a float unless you know what the float multiplied by 100 is, and remember to append f onto the end of your numbers so the compiler can optimise it!
Code: |
class Floater
{
public:
explicit Floater(const float wanted)
{
num = static_cast<int>(wanted*100);
}
Floater(const int wanted)
{
num = wanted;
}
Floater()
{
num = 0;
}
const float ToFloat()const
{
return static_cast<float>(num)/100;
}
const Floater operator+(const Floater& wanted)const
{
return num + wanted.num;
}
const Floater& operator+=(const Floater& wanted)
{
num+=wanted.num;
return *this;
}
const Floater operator-(const Floater& wanted)const
{
return num - wanted.num;
}
const Floater& operator-=(const Floater& wanted)
{
num-=wanted.num;
return *this;
}
const Floater operator*(const Floater& wanted)const
{
return num * wanted.num;
}
const Floater& operator*=(const Floater& wanted)
{
num*=wanted.num;
return *this;
}
const Floater operator/(const Floater& wanted)const
{
return num / wanted.num;
}
const Floater& operator/=(const Floater& wanted)
{
num/=wanted.num;
return *this;
}
const bool operator==(const Floater& wanted)
{
return num==wanted.num;
}
const bool operator!=(const Floater& wanted)
{
return num!=wanted.num;
}
const bool operator<=(const Floater& wanted)
{
return num<=wanted.num;
}
const bool operator>=(const Floater& wanted)
{
return num>=wanted.num;
}
const bool operator<(const Floater& wanted)
{
return num<wanted.num;
}
const bool operator>(const Floater& wanted)
{
return num>wanted.num;
}
private:
int num;
}; |
_________________
www.hungrydeveloper.com
Version 2.0 now up - guaranteed at least 100% more pleasing!
#7257 - Sweex - Fri Jun 13, 2003 11:14 am
Very nice! I'm considering using such a thing in my libs now!:)
I'd only multiply it with 128 or 256 instead of a hundred. If you do that, you can use your floater in cases where having the integral part of it is enough. You can have a ToInt() function like this:
Code: |
const int ToInt() const
{
return num >> 7; // 7 for 128
}
|
#7264 - col - Fri Jun 13, 2003 1:05 pm
yes, you need to use a binary fixed point instead of a decimal one - a divide on gba takes a _long_ time: 70 - 500 cycles depending on how good the divide code is and where it is executed... using a right shift is very cheap (or free depending on the context)...
Also as far as i can tell, the results of your multiplies will be 100 times bigger than they should be (divides will be 100 times smaller) - this could give you some accuracy problems ;)
cheers
Col
#7271 - MumblyJoe - Fri Jun 13, 2003 3:33 pm
I have some other ideas for it too, such as templates for what type of storage is used, and a dynamic way to specify how many places it is saved as. That way it could have more than 2 places, but the user would have to way up the odds on whether they want alot of places or a larger number. The template would also allow users to choose between signed and unsigned numbers.
_________________
www.hungrydeveloper.com
Version 2.0 now up - guaranteed at least 100% more pleasing!
#7276 - col - Fri Jun 13, 2003 5:13 pm
MumblyJoe wrote: |
I have some other ideas for it too, such as templates for what type of storage is used, and a dynamic way to specify how many places it is saved as. That way it could have more than 2 places, but the user would have to way up the odds on whether they want alot of places or a larger number. The template would also allow users to choose between signed and unsigned numbers. |
erm... actually, i think you are missing the point (groan) - as has been suggested using decimal/base10 is VERY inefficient, you should use base2 and ditch the multiplies and divides in your conversions. The gba dosn't use an athlonXP 2Ghz - its only 16mhz. I'm also dubious of the benifits of wrapping fixed point numbers up in a class - most of the time using fixed point is almost transparent in the code:
first using ints :
s32 c = a*b;
a = b + c;
now the same code using fixed point 24.8 : //typedef s16 Fxd8 or just use s16 and remember theyre fixed :)
Fxd8 c = (a*b)>>8;
a = b + c;
- when its not, its usually best to customise things for the context they're in anyway - remember 16mhz!! - the beauty of fixed point on arm chips is that it is as fast or almost as fast as integer... you lose that efficiency if you try using base10, or if you try to wrap it in a class that allows variable point position... you won't be able to write an efficient wrapper for fixed point until you fully understand it... when that happens, you'll probably ditch the idea of wrapping it in a class (other than as an interesting excercise :))
i hope i managed to get my point accross!... (hiss boo) :)
cheers
Col
#7294 - MumblyJoe - Sat Jun 14, 2003 3:27 am
OK, forgive me if I am wrong but I think I have the point pretty well. Im not sure if I understood the correct context of what col said:
Quote: |
as has been suggested using decimal/base10 is VERY inefficient, you should use base2 and ditch the multiplies and divides in your conversions. |
Um... using decimal/base10 is exactly the same as using base2 because base10 is just a nice way for us to look at numbers, they end up exactly the same in memory regardles. If what you meant is that I should use shifts instead of divides and multiplications, then yes I probably should in lots of my code but I leave that for the optimisation stage, if it is needed at all.
Also I see no harm in wrapping anything in a class. col said:
Quote: |
typedef s16 Fxd8 or just use s16 and remember theyre fixed :) |
First of all, typedefs do not make it a different thing at all, so whats the point of making a Fxd8 name. Secondly, if you have to remember they are fixed, then you have to place code in whereever you want to convert them to a useable float, code that wouldnt be much more efficient than an inline non-virtual member function, and is definately not as nice to read or maintain as a class with overloaded operators.
Col also wrote:
Quote: |
I'm also dubious of the benifits of wrapping fixed point numbers up in a class - most of the time using fixed point is almost transparent in the code: |
Well it is meant to be transparent in the code, thats the point of the code. It uses an int internally and doesnt make you change your code outside because you still multiply it etc as you would with a float, and then only use a function to convert it back to a float when needed.
Sorry to rant, and I'm not picking on you col. I admit I may be wrong about some of these things, or misunderstood your point, but I hate the C style of doing things, it's dead, time to move on to the C++ world.
_________________
www.hungrydeveloper.com
Version 2.0 now up - guaranteed at least 100% more pleasing!
#7305 - Lupin - Sat Jun 14, 2003 1:04 pm
nice code mumbly, but I think you should use more optimized code, because otherwise it wouldn't make any sense to me by using such an class...
I found some nice optimized routines for multiplying and such on gbadev.net, you just have to collect them from the code samples and put the into your code somehow :)
I have another question: Why is inline asm so rarely used within GCC code? I think it would optimize some stuff and you wouldn't have to create an whole new .o for asm routines
#7306 - Sweex - Sat Jun 14, 2003 2:35 pm
MumblyJoe; I think what Col tries to point out, is that it is often easier and faster to do a simple shift instead of using your fixed class everywhere.
That doesn't seem like a big problem to me. Question is how often you will actually need an actual floating point value... Sure, it is nice to have a fixed point class, but on a platform like GBA, you need to favour speed over nice code up to a certain degree.
But, I'm not gonna try to convince anyone to do things a certain way, so I'll leave it up to you!:)
#7311 - Lupin - Sat Jun 14, 2003 7:11 pm
I don't think that there's an big difference in using an class or only using shifts, if you do the shifting within the class it should nearly be the same (just with that class stuff added)
#7314 - col - Sat Jun 14, 2003 8:18 pm
MumblyJoe wrote: |
Um... using decimal/base10 is exactly the same as using base2 because base10 is just a nice way for us to look at numbers, they end up exactly the same in memory regardles. If what you meant is that I should use shifts instead of divides and multiplications, then yes I probably should in lots of my code but I leave that for the optimisation stage, if it is needed at all.
|
while the values represented by base10 and base2 systems are the same, using base10 in a computer program is NOT exactly the same as using base2! - and when using fixed point numbers using different radices, they do not and up the same in memory either.
Trying to use shifts to convert base10 fixed point numbers is nonsensical, i think you need to go read some books :)
As far as optimisation goes, using base2 for fixed point numbers in a binary computer is not an optimization, its the only sane way to go - anything else is just pointless...
Quote: |
First of all, typedefs do not make it a different thing at all, so whats the point of making a Fxd8 name.
|
The whole reason for typedefs is to make your code clearer and easier for you and for others to read - so if you are going to use a u16 as a fixed point number, why not call it a fxd so that your intention is clear? you might go further and appent the variable name with f8 or somthing:
Fxd8 f8MyVar;
this way its obvious throughout the code that this variable is a 24.8 fixed point number...
Quote: |
Secondly, if you have to remember they are fixed, then you have to place code in whereever you want to convert them to a useable float, code that wouldnt be much more efficient than an inline non-virtual member function, and is definately not as nice to read or maintain as a class with overloaded operators.
|
Agreed, you would have to use an explicit function call to convert to a float - and that is a good thing, it tells whoever is reading the code that the code is about to get really slow :) - using encapsulation and operator overloading could totally hide this important fact from the poor chap(chapess) who is trying to maintain the code!!
And anyway, i have yet to come accross the need to convert anything into a float when programming the gba - and i'll be surprised if i ever do :)
If i ever do need to use floats, it will probably be in the form of some sort of custom float class that takes advantage of the ARM cpu instructin set...
Quote: |
Col also wrote:
Quote: | I'm also dubious of the benifits of wrapping fixed point numbers up in a class - most of the time using fixed point is almost transparent in the code: |
Well it is meant to be transparent in the code, thats the point of the code. It uses an int internally and doesnt make you change your code outside because you still multiply it etc as you would with a float, and then only use a function to convert it back to a float when needed.
|
And it nicely hides the fact that when someone uses your code to multiply two fixed point numbers together (assuming that you have re-written operator* so that it gives the correct answer), that they will incur the massive overhead of an un-optimised 32bit software divide - ouch
Quote: |
Sorry to rant, and I'm not picking on you col. I admit I may be wrong about some of these things, or misunderstood your point, but I hate the C style of doing things, it's dead, time to move on to the C++ world. |
no problem - its a shame you are emotional and not analytical when making programming decissions :)
personally i don't hate C - and in fact theres no such thing as the 'C style of doing things' - you can use oop in C if you want - its not difficult if you fully understand why oop is important and how it works.
I use C++ (and Arm asm) myself, but when coding for gba, I limit myself to a subset of the language so that i can keep the binaries small and fast. Sometimes I wish I was using C - but usually i'm glad to be using C++
cheers
Col
#7317 - Cyberman - Sat Jun 14, 2003 10:52 pm
Sweex wrote: |
MumblyJoe; I think what Col tries to point out, is that it is often easier and faster to do a simple shift instead of using your fixed class everywhere.
That doesn't seem like a big problem to me. Question is how often you will actually need an actual floating point value... Sure, it is nice to have a fixed point class, but on a platform like GBA, you need to favour speed over nice code up to a certain degree.
But, I'm not gonna try to convince anyone to do things a certain way, so I'll leave it up to you!:) |
GCC's implementation of inline assembly has a lot to be desired namely ease of use. Granted it's 'standardized' it still has a lot of short comings. I myself have considered using it but dealing with the complex syntax and it's kind of difficult to use structuring, it's a sort of wash. My only thought is just creating macros with assembler instructions in them to hide all of what I term INSANITY. GCC is great pretty much on everything but that.
I find this much easier to handle myself:
Code: |
int div(int Dividend, int Divisor)
{
int register Val;
asm
{
mov r0, Dividend
mov r1, Divisor
' Evil division code goes here ;)
mov Val,r0
}
return Val;
}
|
or whatever, unfortunately I completely understand why the inline assembly was done the way it was. I just think it needs a lot of revamping as it's easier to code things in assembly in a seperate module all together to be honest.
As for floating point to fix point that class simply won't work the way you hope it will. I suggest downloading this: Fixed Point App Note from arm.
Cyb
#7377 - Jason Wilkins - Mon Jun 16, 2003 3:35 pm
There is absolutely nothing wrong with a using a fixed point class. It can be written so that it results in the exact same code you would write in C. Using a template, you could even instantiate classes with different binary points.
The benefit is that it is much easier to read and debug than code filled with shifts or function calls.
Things like fixed point numbers are the very reason that operator overloading was added to C++, it is really the best reason to have it, if you are not going to use it for that, then there is no reason for operator overloading to even exist.
_________________
http://devkitadv.sourceforge.net
#7378 - Jason Wilkins - Mon Jun 16, 2003 3:36 pm
There is absolutely nothing wrong with a using a fixed point class. It can be written so that it results in the exact same code you would write in C. Using a template, you could even instantiate classes with different binary points.
The benefit is that it is much easier to read and debug than code filled with shifts or function calls.
Things like fixed point numbers are the very reason that operator overloading was added to C++, it is really the best reason to have it, if you are not going to use it for that, then there is no reason for operator overloading to even exist.
_________________
http://devkitadv.sourceforge.net