Since we know that 0.1 + 0.2 != 0.3
due to limited number representation, we need to instead check hat abs(0.1+0.2 - 0.3) < ε
. The question is, what ε value should we generally choose for different types? Is it possible to estimate it depending on the number of bits and the number and types of operations that are likely to be performed?
The EPSILON property has a value of approximately 2.2204460492503130808472633361816E-16 , or 2-52.
In general, if you look at a machine number with base b, mantissa m (and exponent e), you can define eps:=b1−m2. To your example: You would probably represent 4 normalized as (0.10000000)2⋅23. The next number 4+132 is then represented as (0.10000001)2⋅23, i.e. you have m=8 and thus eps=2−8.
Epsilon comparisonsbool isEqual = fabs(f1 – f2) <= epsilon; With this calculation we can express the concept of two floats being close enough that we want to consider them to be equal.
By comparing floats to epsilon instead of zero, we're able to approximate their values and account for the minor rounding errors present in the floating point definition. Epsilon can be used to account for all kinds of inaccuracies with regards to floats, not just a comparison to zero.
A baseline value for epsilon is the difference between 1.0
and the next highest representable value. In C++, this value is available as std::numeric_limits<T>::epsilon()
.
Note that, at the minimum, you need to scale this value as a proportion of the actual number you're testing. Also, since the precision scales only roughly with the numeric value, you may want to increase your margin by a small factor to prevent spurious errors:
double epsilon = std::numeric_limits<double>::epsilon();
// C++ literals and math functions are double by default
bool is_near = abs(0.1+0.2 - 0.3) <= 0.3 * (2*epsilon);
As a more complete example, a function for comparing doubles:
bool is_approximately_equal(double a, double b) {
double scale = max(abs(a), abs(b));
return abs(a - b) <= scale * (2*epsilon);
}
In practice, the actual epsilon value you should use depends on what you're doing, and what kind of tolerance you actually need. Numeric algorithms will typically have precision tolerances (average and maximum) as well as time and space estimates. But the precision estimate typically starts with something like characteristic_value * epsilon
.
You can estimate the machine epsilon using the algorithm below. You need to multiply this epsilon with the integer value of 1+(log(number)/log(2))
. After you have determined this value for all numbers in your equation, you can use error analysis to estimate the epsilon value for a specific calculation.
epsilon=1.0
while (1.0 + (epsilon/2.0) > 1.0) {
epsilon = epsilon /2.0
}
//Calculate error using error analysis for a + b
epsilon_equation=Math.sqrt(2*epsilon*epsilon)
document.write('Epsilon: ' + epsilon_equation+'<br>')
document.write('Floating point error: ' + Math.abs(0.2 + 0.4 -0.6)+'<br>')
document.write('Comparison using epsilon: ')
document.write(Math.abs(0.2 + 0.4 -0.6)<epsilon_equation)
Following your comment, I have tried the same approach in C# and it seems to work:
using System;
namespace ConsoleApplication
{
public class Program
{
public static void Main(string[] args)
{
double epsilon = 1.0;
while (1.0 + (epsilon/2.0) > 1.0)
{
epsilon = epsilon/2.0;
}
double epsilon_equation = Math.Sqrt(2*epsilon*epsilon);
Console.WriteLine(Math.Abs(1.0 + 2.0 - 3.0) < Math.Sqrt(3.0 * epsilon_equation * epsilon_equation));
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With