Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » AGAIN floats

This thread is locked; no one can reply to it. rss feed Print
 1   2 
AGAIN floats
Frank Drebin
Member #2,987
December 2002
avatar

i want to write a function

bool floats_are_equal(float a, float b)
{
   return (fabs(a-b)<float_error);
}

with float_error=0.000001 because floats have 6 decimals or is there a better value i can take for this (do have floats a maximum inaccuracy i should take?)?

Evert
Member #794
November 2000
avatar

Quote:

because floats have 6 decimals

Not exactly. Floats can store numbers with a certain precision (don't know if that's six or more, but that doesn't really matter), that precission does not depend on the order of magnitude of the number (because it is a floating-point number).

Depending on your needs, 0.000001 could be a sufficient check. It really depends on what you want to do with it.

spellcaster
Member #1,493
September 2001
avatar

First of all, you need to turn around the operator, or it'll return true if the floats are not equal.
And I'd say use something which makes sense in your game. If you're using 4 decimal digits (internally) use 0,00005 as error.

--
There are no stupid questions, but there are a lot of inquisitive idiots.

damage
Member #3,438
April 2003

If you are using C++, do this.

#include <limits>

using namespace std; 

...

cout << "Float has " << numeric_limits<float>.digits10 << " digits" << endl

Secondly, don't use float. Use double, you get more precision for free, I am pretty sure.

____
Don't have anything private. Don't do anything silly like having a hidden name and address field with get_name and set_address and get_name and set_name functions. - Bjarne Stroustrop, creator of C++

Evert
Member #794
November 2000
avatar

doubles may be slower than floats (usually not an issue on modern hardware).

gnolam
Member #2,030
March 2002
avatar

damage said:

Use double, you get more precision for free, I am pretty sure.

It's a trade-off between accuracy and speed.

Frank Drebin said:

with float_error=0.000001 because floats have 6 decimals or is there a better value i can take for this (do have floats a maximum inaccuracy i should take?)?

http://www.infosys.utas.edu.au/info/documentation/C/CStdLib.html#float.h
For further information, consult your local float.h ...

--
Move to the Democratic People's Republic of Vivendi Universal (formerly known as Sweden) - officially democracy- and privacy-free since 2008-06-18!

Chris Katko
Member #1,881
January 2002
avatar

To my knowledge, doubles will normally be slower then floats until we go 64bit processors (doubles are 64bit). Unless in the case of using MMX, but then you have to reset the MMX registers and that takes up most of the gain, unless your doing batches of floating-point numbers.

I may be wrong. I haven't been able to code in a long time so it may or may not be correct.

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

damage
Member #3,438
April 2003

Quote:

doubles may be slower than floats (usually not an issue on modern hardware).

That's true, but the vast majority of us will be programming on an x86 pentium or greater. From the pentium onward double and float have exactly the same speed, AFAICT.

____
Don't have anything private. Don't do anything silly like having a hidden name and address field with get_name and set_address and get_name and set_name functions. - Bjarne Stroustrop, creator of C++

A J
Member #3,025
December 2002
avatar

does the libc have #DEFINEs for floating point error for operations on floats ?

___________________________
The more you talk, the more AJ is right. - ML

Frank Drebin
Member #2,987
December 2002
avatar

the question isn't wehter using floats or doubles.
i'm going to use floats. but what value should i take??? (p.s. is FLT_EPSILON in the float.h the maximum inaccurcay of floats?)?

Mars
Member #971
February 2001
avatar

Quote:

the question isn't wehter using floats or doubles.

Who are you blaming? They stayed relatively close to the topic! ;D

Quote:

i'm going to use floats. but what value should i take

You already have got some good replies. It depends on your application. If you're programming a game, then you need to take into consideration the accuracy of your engine.

--
This posting is a natural product. The slight variations in spelling and grammar enhance its individual character and beauty and in no way are to be considered flaws or defects.

X-G
Member #856
December 2000
avatar

I'm still trying to figure out why exactly you need to do this comparison. So far, I've never had to do == checks on floats or doubles, simply because I've never come across any situation that would require it.

--
Since 2008-Jun-18, democracy in Sweden is dead. | 悪霊退散!悪霊退散!怨霊、物の怪、困った時は ドーマン!セーマン!ドーマン!セーマン! 直ぐに呼びましょう陰陽師レッツゴー!

Plucky
Member #1,346
May 2001
avatar

Try:

bool floats_are_equal(float a, float b)
{
   return (fabs(a-b)/fabs(a) <= FLT_EPSILON) &&
          (fabs(a-b)/fabs(b) <= FLT_EPSILON);
     // && can be replaced with || for a more relaxed comparison
}

Should solve problems with scale issues and over/underflow issues.

Frank Drebin
Member #2,987
December 2002
avatar

i use this because my coordinates of objects are stored in floats and because of movement and collision detection i have to compare them!!!

--> so there are max 3 places before the point and now when there are 3 places behind the point left -> i should take a value like 0.001 ?!?

X-G
Member #856
December 2000
avatar

Compare then, yes - but why == ? Collision detection usually just involves <= and >= ...

--
Since 2008-Jun-18, democracy in Sweden is dead. | 悪霊退散!悪霊退散!怨霊、物の怪、困った時は ドーマン!セーマン!ドーマン!セーマン! 直ぐに呼びましょう陰陽師レッツゴー!

Frank Drebin
Member #2,987
December 2002
avatar

that's true but for movement ( while (x_pos!=x_pos_to_reach) move_x();) or something like this sometimes ==/!= is nice.

X-G
Member #856
December 2000
avatar

You could use use a threshold for that purpose and use </>.

--
Since 2008-Jun-18, democracy in Sweden is dead. | 悪霊退散!悪霊退散!怨霊、物の怪、困った時は ドーマン!セーマン!ドーマン!セーマン! 直ぐに呼びましょう陰陽師レッツゴー!

Fladimir da Gorf
Member #1,565
October 2001
avatar

You can use, for example:

if( x_pos > x_pos_to_reach )
 while( x_pos < x_pos_to_reach ) move_x();
else while( x_pos > x_pos_to_reach ) move_x();

I've never, ever yet needed to compare floats, and I don't think I'll ever need to.

OpenLayer has reached a random SVN version number ;) | Online manual | Installation video!| MSVC projects now possible with cmake | Now alvailable as a Dev-C++ Devpack! (Thanks to Kotori)

Frank Drebin
Member #2,987
December 2002
avatar

yes but i think mine is a bit shorter (easier).
i don't like floats very much too but you have to use them for smooth movement (except scaled ints)

Fladimir da Gorf
Member #1,565
October 2001
avatar

"i don't like floats very much too "

Who said I didn't like floats? When was the last time I didn't use floats (or doubles) for movement? I don't like comparasions between them, just because you may not reach the destination, depending on the speed of the object.

"yes but i think mine is a bit shorter (easier)."

Yes, but mine works.

OpenLayer has reached a random SVN version number ;) | Online manual | Installation video!| MSVC projects now possible with cmake | Now alvailable as a Dev-C++ Devpack! (Thanks to Kotori)

Andrei Ellman
Member #3,434
April 2003

evert said:

Quote:
--------------------------------------------------------------------------------
because floats have 6 decimals
--------------------------------------------------------------------------------
Not exactly. Floats can store numbers with a certain precision (don't know if that's six or more, but that doesn't really matter), that precission does not depend on the order of magnitude of the number (because it is a floating-point number).

An IEEE-754 floating point number is represented by a sign-bit, an exponent, and a mantissa. The precision of a floating point number depends only on the number of bits in the mantissa-portion, and the range of it's order of mangitude depends on the number of bits in the exponent, so talking about the "number of decimals" does not make sense with floating point numbers (unless the exponent is always the same - hence 'fixed-point numbers'), but the "number of significant digits" does.

A floating point number is worked out from it's stored representation using

( 1 - 2*sign ) * 2^(exp - exp-bias) * ( 1 + mantissa / mantissa-divisor )
The exp-bias and mantissa-divisor are used to maximise the number of possible values that can be stored with this representation. The +1 for the mantissa and the exp-bias are used to normailse the number.

A single precision floating point number (a 32-bit 'float') uses 1 sign-bit, 8 exponent-bits and 23 mantissa-bits, so exp-bias would be 127 (2^8 - 1 - 2^(8-1)) and mantissa-divisor would be 8388608 (2^23).
A double precision floating point number (a 64-bit 'double') uses uses 1 sign-bit, 11 exponent-bits and 52 mantissa-bits, so exp-bias would be 1023 (2^11 - 1 - 2^(11-1)) and mantissa-divisor would be 4503599627370496 (2^52).

The number of significant digits in base 10 a floating point number can store is calculated as:
ceil(log10(2^(mantissa_bits+1)));
The +1 was added because the floating point format assumes the first bit of the mantissa is always 1 (an optimisation that can be done using base-2 numbers).

For more information and much better explanations than what I've written here, see the following web-pages:

</li>
Do some Googling for even more...

etwinox said:

To my knowledge, doubles will normally be slower then floats until we go 64bit processors (doubles are 64bit). Unless in the case of using MMX, but then you have to reset the MMX registers and that takes up most of the gain, unless your doing batches of floating-point numbers.

The MMX is used to do similar operations in paralell on sets of data, but MMX only works for integers (or fixed point numbers with tweaking of the input/output). MMX does not work with floats/doubles.

[edit2:] SSE which is a newer extension than MMX is like MMX but works with floats. SSE2 (only found so far on Pentium 4's) is similar but uses doubles.

damage said:

Quote:
--------------------------------------------------------------------------------
doubles may be slower than floats (usually not an issue on modern hardware).
--------------------------------------------------------------------------------
That's true, but the vast majority of us will be programming on an x86 pentium or greater. From the pentium onward double and float have exactly the same speed, AFAICT.

I think that internally, the Pentium will perform operations at the same speed on doubles and floats as long as the values are stored within the CPU's FPU registers (not sure on this). What I am sure of is that on a 32-bit processor (IA32) such as the Pentium and many of it's successors, there is only a 32-bit bus between the CPU and memory, so the CPU can read/write a 32-bit float (32-bits) in one memory-fetch, and it takes two memory-fetches to read/write a 64-bit double to memory, and three to read an 80-bit number (question: is this what a 'long double' is or does it have even more bits?). On an IA64 processor, it can work with doubles and floats at the same speed (unless there's some sort of optimisation that involves reading in two adjacent floats in memory at once).

Also, the x87 FPU (floating point component of any x86-based chip) uses 32-bits for short floating-point numbers, and 80-bits for long floating point numbers. I don't think it can natively work with 64-bit numbers without expanding them to 80 bits. This means that the x87 FPU will not generate overflows or underflows that would be generated by doing the same calculations using a 64-bit FPU.

Plucky said:

Try:
bool floats_are_equal(float a, float b)
{
return (fabs(a-b)/fabs(a) <= FLT_EPSILON) &&
(fabs(a-b)/fabs(b) <= FLT_EPSILON);
// && can be replaced with || for a more relaxed comparison
}
Should solve problems with scale issues and over/underflow issues.

The above code uses divides. That will significantly slow down any code that does a lot of comparisons. IMO, that's just overkill!

[edit1:] perhaps an optimised version of this could be written that works by adding to or subtracting from the bits of the IEEE-754 float's exponent instead of using divides, but that would make it non-portable.

AE.

--
Don't let the illegitimates turn you into carbon.

Plucky
Member #1,346
May 2001
avatar

Quote:

The above code uses divides. That will significantly slow down any code that does a lot of comparisons. IMO, that's just overkill!

The code, or something like it, is necessary if you want to compare floats across its whole dynamic range. Otherwise something like what Frank first suggested would work if you knew what range all the float comparisons would be operate in.

[edit] Perhaps we should add floating point questions to the list of questions that pop up weekly or bi-weekly. :) I bet we already have a dozen posts that go through the details of the floating point implementation.

Andrei Ellman
Member #3,434
April 2003

Plucky: See the edit1 to the above post for a possible means of optimising it.

--
Don't let the illegitimates turn you into carbon.

Plucky
Member #1,346
May 2001
avatar

I'm not sure playing with the bits and probably adding one or two conditionals to handle special cases would be faster. Someone would of course need to code both methods and test them to be sure.

Korval
Member #1,538
September 2001
avatar

Quote:

that's true but for movement (
while (x_pos!=x_pos_to_reach) move_x();
) or something like this sometimes ==/!= is nice.

You aren't looking for a generalized ==/!= floating-point test. What you want is to test whether or not the given Vec2 is within a particular box. The size of the box depends on the accuracy you care about.

If this is a 2D game, the accuracy you care about is to the nearest pixel. So, just do an "(int)" cast operation to the floats in question.

If this is a 3D game (or, for some reason, you need better-than-per-pixel-accuracy), then I would suggest a more flexible system than, "Is he at position Vec2 yet?" I would, instead, suggest that all movements be normalized on the range 0.0f-1.0f.

Let's say you have the initial position Vec2i, and the final position Vec2f. The direction towards the final position is Normalize(Vec2f-Vec2i). Let's call that Vec2Dir. The distance between these two points is Dist. Given that this is a game, you probably have some speed that the object moves. Let us call that Speed.

The objective now is to create a function F(t), such that F(0.0f) == Vec2i, and F(1.0f) == Vec2f. Why is this good? Because it makes telling when you're done with the movement trivial. You just clamp 't' such that it is never greater than 1.0f. When t==1.0f (a perfectly fine comparison, considering that your clamp function just set it to 1.0f), the movement is finished.

So, what is F(t)? Well, F(t) = Vec2i + (Vec2Dir * Dist * t). Simple enough, right?

The Vec2Dir gets it pointing in the right direction. Dist * Vec2Dir + Vec2i == Vec2f (given the definitions of Dist and Vec2Dir). And t=0 means that F is Vec2i. Just what we need.

Of course, it isn't precisely what you need, because the time scale of t is all wrong. You have some speed for the object. And you're taking particular timesteps. Therefore, you need to be able to feed in a time delta into F(t). Which means, you need a new function t(time) such that you can build the useful function G(time).

Well, given the speed Speed of the object, you know that it will take TimeScale = Dist / Speed seconds to go from the initial position to the final. As such, the function t(time) = time / TimeScale.

Therefore:

G(time) = Vec2i + (Vec2Dir * Dist * (time / TimeScale));

What if the speed changes during the travels? Well, you will have to reset the variables every time the speed changes. Vec2i becomes the current location, and you have to recompute Dist and TimeScale (Vec2Dir shouldn't have changed, but you get it for free).

Obviously, if the object isn't going in a straight line from point A to point B, that's a different issue. In that case, use the box method, and pick whatever accuracy you need (tenths, 0.001, etc). Do not, however, think of it as a generalized floating-point equality test. This is to be used only for the purpose of determining whether a particular Vec2 is at a particular position in the world. This function should be used only for that.

 1   2 


Go to: