Tuesday 4 October 2011

Equality Among Floats

Well the Gladius Math library is coming along swimmingly and is taking shape very quickly now that we have the API outline almost complete.  (https://github.com/alankligman/gladius.math) Check out the wiki section.

After implementing the functions for 2 dimensional vectors [x,y] an interesting issue presented itself in the form of one glaring logic error and a unit test case issue.
  1. In our library we have a function, normalize(v1, result); which returns a unit vector with same direction as the given vector v1.  Result is the optional parameter in which we store the resulting vector.  Our code for this function was as follows:
// Return a Vector(result) with same direction as v having unit length
normalize: function( v, result ) {
   for( var i = 0, len = v.length; i < len; ++ i ) {
     result[i] = v[i] / len;
   }

   return result;
}
 Normalizing a vector is done by calculating its length and dividing each component by the length.  At first glance this code looks to be correct but we will quickly see how a small flaw ruins any results this function returns.
The problem caused by len = v.length. When we use v.length it is in fact returning the length of the array holding our vector, in this case [x,y] which would cause the function to just divide components by 2.
By changing the code to  len = vector.length(v); we open up the next set of problems.  Vector lengths (http://en.wikipedia.org/wiki/Euclidean_vector#Length)
  • By calculating the absolute length of vectors, some pretty unpleasant numbers can be returned as a result.  For example, when we call normalize() for the vector v1 = [12,-5] the resulting vector is expressed as v2 = [(12/13), (-5/13)].  This would not be a problem if a computer were able to show the results as the above expression but of course it can not and it will return a float with a set number of decimals.
  • This problem is compounded when you start to add other operations on the resulting vector which will continue to use the float representation of the result.  This is very evident in the unit tests:
 You can see above that the expected results and the computed results are identical up until the sixth digit after the decimal.  For all intensive purposes this is "equal", we now have to add an equal(v1, v2, e) function which will compare 2 vectors up to a set number of decimals for use to call them equal.

And that is what i'm working on now, as well as figuring out how to compare only a set number of decimals in the Qunit tests.

No comments:

Post a Comment