How To: Compare Floating-Point Numbers |
This section shows how to safely compare floating-point numbers.
This topic contains the following sections:
This library contains a class Numeric that supports proper comparison of floating-point values. The comparisons are performed by checking if two values differ by less than a tolerance value epsilon. (Note: The class Numeric is part of the DigitalRune.dll not the DigitalRune.Mathematics.dll.)
Following code shows a simple example where comparison with == is not desired.
// Lets do some arbitrary calculations. float a = (float)Math.PI; float b = (float)Math.Sqrt(a); float c = (float)Math.Sqrt(b); float d = c * c * c * c; // --> d should be equal to a, but because of limited precision and numerical errors a != d. if (a == d) Console.WriteLine("a == d"); else Console.WriteLine("a != d"); // This message is written. :-( // Comparison methods in Numeric allow a small error. if (Numeric.AreEqual(a, d)) Console.WriteLine("Numeric.AreEqual(a, d) is true."); // This message is written. :-) else Console.WriteLine("Numeric.AreEqual(a, d) is false.");
Numeric has several other methods for comparison of floating-point numbers (single or double precision).
The values EpsilonF and EpsilonD in Numeric define the allowed error for the numerical comparison methods for single- and double-precision floating-point numbers. These values are used in the methods of Numeric - unless the method allows to specify a different epsilon as a method parameter.
These epsilon values have been optimized for typical 3D simulations and games - including 3D collision detection and rigid body physics simulation.
EpsilonF is used for single-precision floating-point numbers. EpsilonD is used for double-precision floating-point numbers. One common mistake is to invoke a method that uses the wrong epsilon value:
float myFloat = ...; bool result1 = Numeric.AreEqual(myFloat, Math.Cos(0.3f))); // Warning: This uses EpsilonD because Math.Cos() returns double! bool result2 = Numeric.AreEqual(myFloat, (float)Math.Cos(0.3f))); // This uses EpsilonF!
Several types implement the comparison operators (==, !=, >=, etc. in C#), for example: Vector3F, Matrix44F. These overloaded operations compare the floating-point values directly (without using an epsilon tolerance). In addition to the equality operators most types have methods, like AreNumericallyEqual, that perform comparisons using an epsilon tolerance.
This example compares two vectors:
// Define a vector. Vector3F v0 = new Vector3F(1000, 2000, 3000); // Define a rotation quaternion that rotates 360° around the x axis. QuaternionF rotation = QuaternionF.CreateRotationX(MathHelper.ToRadians(360)); // Rotate v0. Vector3F v1 = rotation.Rotate(v0); // The rotated vector v1 should be identical to v0 because a 360° rotation should not // change the vector. - But due to numerical errors v0 and v1 are not equal. if (v0 == v1) Console.WriteLine("Vectors are equal."); else Console.WriteLine("Vectors are not equal."); // This message is written. :-( // With Vector3F.AreNumericallyEqual() we can check if two vectors are equal when we allow // a small numeric tolerance. if (Vector3F.AreNumericallyEqual(v0, v1)) Console.WriteLine("Vectors are numerically equal."); // This message is written. :-) else Console.WriteLine("Vectors are not numerically equal.");