Sign in to follow this  

Trouble with floating point numbers

This topic is 4106 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I have made a 2D vector class and I am storing its horizontal (X) and vertical (Y) components as floating integers, but I am having trouble with the acuracy. For example, when X is 0, X == 0 or X == 0.0 both return false. This causes a problem in my Build() method, which is supposed to give a vector magnitude and direction (angle in degrees with positive X axis) based on it's X and Y components. Here are the two methods:
    void Vector::Resolve(){
        X = Magnitude * cosd ( Angle );
        Y = Magnitude * sind ( Angle );
    }
    
    void Vector::Build(){
        Magnitude = std::sqrt( pow(X,2) + pow(Y,2) );
        if( X == 0.0 && (Y == 0.0 || Y == -0.0) ){
            //Undefined vector, leave angle 0 for now
            Angle = 0.0;
        } else if ( X == 0.0 && Y > 0.0 ){
            //Vertically upward vector
            Angle = 90;
        } else if ( X == 0.0 && Y < 0.0 ){
            //Vertically downward vector
            Angle = -90;
        } else {
            //X is not zero, find arctan
            Angle = atand( Y / X ); //arctan() returned in degrees
        }
    }

    Vector Vector::operator+ ( Vector Vect ){
        Vector Sum;
        Sum.X = X + Vect.X;
        Sum.Y = Y + Vect.Y;
        Sum.Build();
        return Sum;
    }
       

So suppose I add two vectors opposite in direction, the resultant vector contains X = 0.0 and Y = -0.0, which ideally should get caught in the first if() statement, but it doesn't, and it tries to find the arctan of 0/0 and it returns -90.0, which kinda sucks because the entire motion of my object goes haywire once it gets an angle of -90.0. Any help?

Share this post


Link to post
Share on other sites
Checking for equality with floating-point numbers notoriously doesn't work well. What you want to use is an epsilon check, in which you check to see if the absolute value of the number is less than same other small number:

fabs(X) < 0.0001

A few other things: 1) atan2 is well-defined for points other than the origin, so you only need to check for (0,0) if you use it. The function might already return 0 in that case, but if the documentation says it's undefined I would check just in case. Then you just do a radian-to-degree conversion at the end of the function. 2) You can use X*X and Y*Y instead of pow(X,2) and pow(Y,2) as an optimization.

Share this post


Link to post
Share on other sites
Link 1 (technical, but complete)
Link 2 (Wikipedia)

Rounding error, overflow/underflow, and cancellation are three of the major problems when dealing w/ floating point numbers. You should be familiar with each.

For comparison of FP values, use an epsilon (as Zipster mentioned).

bool isEqual(float fp1, float fp2, float eps)
{
if (fabs(fp1 - fp2) < eps)
return true;
else
return false;
}

Share this post


Link to post
Share on other sites
I don't like the "epsilon" trick. It is hard to decide what a suitable value of epsilon is, and you'll probably run into cases where you wanted the numbers to be considered equal but they are not, or the other way around.

For most things you may want to program, you can limit your comparisons to inequalities, and in a way such that it doesn't really matter if the equal case is classified one way or the other. This type of programming is much more robust.

Share this post


Link to post
Share on other sites
True -- using epsilon becomes more about "are these two values close".

But it does increase robustness by a small amount.

I find it interesting that some alternate calculus' have the concept of "you can't compare two real numbers for equality" (because, really, even with real real numbers, you cannot), and in general restrict operations in ways that are the same as the restrictions programmers have on floating point numbers.

A friend of mine actually wrote up a real real number library -- in haskall (later functional programming language).

A real real number is a function from the rationals to the rationals. Given a rational (let's call it epsilon) it returns a rational that is within epsilon of the result.

He then proceeded to write a ray tracer. @_@

Share this post


Link to post
Share on other sites
Quote:
Original post by alvaro
I don't like the "epsilon" trick. It is hard to decide what a suitable value of epsilon is, and you'll probably run into cases where you wanted the numbers to be considered equal but they are not, or the other way around.


Machine epsilon gives you a good place to start.

Quote:
Original post by alvaro
For most things you may want to program, you can limit your comparisons to inequalities, and in a way such that it doesn't really matter if the equal case is classified one way or the other. This type of programming is much more robust.


I agree that in many cases you can eliminate strict equalities and replace them with inequalities. But this is not always the case.

Share this post


Link to post
Share on other sites
Quote:
Original post by alvaro
I don't like the "epsilon" trick. It is hard to decide what a suitable value of epsilon is, and you'll probably run into cases where you wanted the numbers to be considered equal but they are not, or the other way around.

And for these things you'd use either integers, or correct rounding. (Integers are preferable.)

Share this post


Link to post
Share on other sites
Regarding comparing "close enough" floating point numbers for equality

Reference (not Cast) both floats as integers, and subtract one from the other

For consequtive floats** with the same sign, their bitpattern when referenced as integers will be consecutive ints

**meaning no representable float is inbetween them; theres not enough bits of accuracy to have a smaller delta

in fact if they are not consequtive, the integer difference will represent how many other floats are inbetween them



example code:
This lists the first 20 Representable floats from 1.0

float a;
int * ai;
a=1.0f;
ai=(int*)&a;
for(int i=0;i<20;i++){
*ai=(*ai) +1;
printf("a %5.20f %d\n",a,*ai);
}





P.S. the floats being comared Must have the same sign, nan, -inf, 0, etc fail.

[Edited by - haphazardlynamed on September 14, 2006 9:27:19 PM]

Share this post


Link to post
Share on other sites
Christer Ericson has some good ideas about practical numerical robustness for geometric calculations. You may want to check out his website,

realtimecollisiondetection.net

In particular, look at his 3/26/05 update, the link to his GDC 2006 presentation, part of a day-long tutorial with other presenters as well. Christer's talk focuses on the robustness issues.

His book also is extremely good, if you are interested in a comprehensive discussion of real-time collision detection techniques.

Share this post


Link to post
Share on other sites

This topic is 4106 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this