Vector2D.Normalize() wrong result

1
McGum
1/8/2013 1:08 PM

Hi together,

i call:

var vector = new Vector2D(998,0);
vector.Normalize();

then vector is
X 0.99999999999999989 double
Y 0 double


must be a Bug ;)


Wout
1/8/2013 1:25 PM

Hi,

It's not a bug but a floating point rounding error, it's the result of:

C# Code:
(998d * (1d / System.Math.Sqrt(998d * 998d)))

The division is done once, and then the multiplication is done for both x and y (multiplication is faster than division). In practice this doesn't give any problems.

- Wout

McGum
1/8/2013 2:14 PM

mhhmm ... and, how can I check if a Vector is 1:0? So for me it gives problems :(

rammi
2/18/2013 1:51 PM

When using IEEE floating point math as it is implemented in modern computers you should never rely on exactness.

So always be prepared for a small delta, usually called epsilon.

As you know that both vectors you are comparing are of magnitude 1 the comparision is simple enough:

C# Code:
        /// <summary>
        /// Are two vectors of length <c>1.0</c> equal with only a small difference?
        /// </summary>
        /// <remarks>
        /// Use only for normalized vectors!
        /// </remarks>
        /// <param name="v1">First vector.</param>
        /// <param name="v2">Second vector.</param>
        /// <returns><c>true</c> if both vectors are nearly equal, <c>false</c> otherwise.</returns>
        public static bool AreNearlyEqual(Vector2D v1, Vector2D v2) {
            const double epsilon = 1e-12;
            return System.Math.Abs(v1.X - v2.X) < epsilon && 
                   System.Math.Abs(v1.Y - v2.Y) < epsilon;
        }

Be warned that the value of epsilon should be relative to the magnitude of the numbers you are comparing, as the exactness of the underlying IEEE number representation is relative to the magnitude. So in general you should eg multiply a reasonable epsilon with the maximum length of the compared vectors (or faster: their largest absolut component).
Never use an absolute epsilon when you are not sure of the magnitude of the numbers involved!

The relative epsilon used (here 1e-12 which is a reasonable general-purpose value for doubles) depends on the algorithms involved before comparing the results. Some algorithms introduce errors very fast, and it is not easy to reduce them. CadLib takes care in various places (eg spline calculation) to make algorithms robust by taking measures to not inroduce too large errors.

Hope this helps,
Rammi

rammi
2/18/2013 2:15 PM

Newer versions of CadLib also provide two Vector2D.AreApproxEqual() methods.

The method
bool AreApproxEqual(Vector2D u, Vector2D v)
internally uses an absolute epsilon value, which is only useful for vectors with magnitude 1.0.

So in general you should use
bool AreApproxEqual(Vector2D u, Vector2D v, double tolerance)
with a tolerance which is matching your result space.
For example I often use the bounding box of the model to determine a useful tolerance value.

Regards,
Rammi

1