Saturday, January 20, 2007

Avoid Numerical Error with Strict Floating Point Types

Code is much better off when it takes the discreteness of numerical computation seriously, here is a robust and flexible "Real" class as an example (The following is done using C# style code... but it can be done in any other language just as easily).

It should also be noted that the epsilon value chosen in the example below is somewhat arbitrary. The value at a absolute maximum should be 1E-6, but it is acceptable for values all the way to 1E-12 (and possibly smaller). Just remember that the whole point of it is to provide a work around for the numerical error the results from storing values in a binary mantissa, with a binary exponent. I don't doubt that there is/are thes(is)/(es) on this subject with a statistical study of what a good epsilon is, but for the purposes of a non-critical 3D application I would say you can just fudge it.
struct Real{

public const float EPSILON = 0.0000001f;
private float number;

public float Value{
get{ return this.number; }
set{ this.number = value; }
}

public Real(float num){
this.number = num;
}

public static bool operator==(Real lhs, Real rhs){
if (Abs(lhs.number - rhs.number) < EPSILON){
return true;
}
return false;
}

// Implement "Equals" if Java or C# using == operator
// ...

public static Real operator/(Real lhs, Real rhs){
ASSERT(Abs(rhs.number) >= EPSILON);
Real result = new Real(lhs.number/rhs.number);
return result;
}
public static Real operator*(Real lhs, Real rhs){
Real result = new Real(lhs*rhs);
return result;
}
// ... all other operators

}