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
?
The difference between
X
and the next value ofX
varies according toX
.epsilon()
is only the difference between1
and the next value of1
.The difference between
0
and the next value of0
is notepsilon()
.Instead you can use
std::nextafter
to compare a double value with0
as the following:So let's say system cannot distinguish 1.000000000000000000000 and 1.000000000000000000001. that is 1.0 and 1.0 + 1e-20. Do you think there still are some values that can be represented between -1e-20 and +1e-20?
I think that depend on the precision of your computer. Take a look on this table: you can see that if your epsilon is represented by double, but your precision is higher, the comparison is not equivalent to
Good question anyway!
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
is1.001 * 10^0
, andepsilon
is1.000 * 10^-3
. But1.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 ofepsilon
.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 thatepsilon
is a reasonable estimate of the error in the calculation that producedsomeValue
, and it may be that it isn't.An aproximation of epsilon (smallest possible difference) around a number (1.0, 0.0, ...) can be printed with the following program. It prints the following output:
epsilon for 0.0 is 4.940656e-324
epsilon for 1.0 is 2.220446e-16
A little thinking makes it clear, that the epsilon gets smaller the more smaller the number is we use for looking at its epsilon-value, because the exponent can adjust to the size of that number.
There are numbers that exist between 0 and epsilon because epsilon is the difference between 1 and the next highest number that can be represented above 1 and not the difference between 0 and the next highest number that can be represented above 0 (if it were, that code would do very little):-
Using a debugger, stop the program at the end of main and look at the results and you'll see that epsilon / 2 is distinct from epsilon, zero and one.
So this function takes values between +/- epsilon and makes them zero.