Compare double to zero using epsilon
Today, I was looking through some C++ code (written by somebody else) and found this section:
double someValue = ...
if (someValue < std::numeric_limits<double>::epsilon() &&
someValue > -std::numeric_limits<double>::epsilon()) {
someValue = 0.0;
}
I'm trying to figure out whether this even makes sense.
The documentation for epsilon()
says:
The function returns the difference between 1 and the smallest value greater than 1 that is representable [by a double].
Does this apply to 0 as well, i.e. epsilon()
is the smallest value greater than 0? Or are there numbers between 0
and 0 + epsilon
that can be represented by a double
?
If not, then isn't the comparison equivalent to someValue == 0.0
?
Assuming 64-bit IEEE double, there is a 52-bit mantissa and 11-bit exponent. Let's break it to bits:
1.0000 00000000 00000000 00000000 00000000 00000000 00000000 × 2^0 = 1
The smallest representable number greater than 1:
1.0000 00000000 00000000 00000000 00000000 00000000 00000001 × 2^0 = 1 + 2^-52
Therefore:
epsilon = (1 + 2^-52) - 1 = 2^-52
Are there any numbers between 0 and epsilon? Plenty... E.g. the minimal positive representable (normal) number is:
1.0000 00000000 00000000 00000000 00000000 00000000 00000000 × 2^-1022 = 2^-1022
In fact there are (1022 - 52 + 1)×2^52 = 4372995238176751616
numbers between 0 and epsilon, which is 47% of all the positive representable numbers...
The test certainly is not the same as someValue == 0
. The whole idea of floating-point numbers is that they store an exponent and a significand. They therefore represent a value with a certain number of binary significant figures of precision (53 in the case of an IEEE double). The representable values are much more densely packed near 0 than they are near 1.
To use a more familiar decimal system, suppose you store a decimal value "to 4 significant figures" with exponent. Then the next representable value greater than 1
is 1.001 * 10^0
, and epsilon
is 1.000 * 10^-3
. But 1.000 * 10^-4
is also representable, assuming that the exponent can store -4. You can take my word for it that an IEEE double can store exponents less than the exponent of epsilon
.
You can't tell from this code alone whether it makes sense or not to use epsilon
specifically as the bound, you need to look at the context. It may be that epsilon
is a reasonable estimate of the error in the calculation that produced someValue
, and it may be that it isn't.