If I'm not wrong the definition of the Machine Epsilon is the lowest number that satisfies the condition:
I was trying to test this making use of the std::numeric_limits<float>::epsilon()
but the value does not satisty this, if you try to get the prior float number with std::nextafter
:
#include <cmath>
#include <iostream>
#include <limits>
int main() {
float e = std::numeric_limits<float>::epsilon();
float previous = std::nextafter(e, -std::numeric_limits<float>::infinity());
std::cout << std::boolalpha << ((1.0f + previous) > 1.0f) << std::endl;
return 0;
}
This stills outputs true
https://coliru.stacked-crooked.com/a/841e19dafcf0bf6f.
After trying to get the number using std::nextafter
I noticed that the proper Machine Epsilon should be:
std::nextafter(std::numeric_limits<float>::epsilon() / 2.0f, std::numeric_limits<float>::infinity())
I tested it using this code:
#include <cmath>
#include <iostream>
#include <limits>
bool verify(float e) {
return ((1.0f + e) > 1.0f);
}
int main() {
std::cout.precision(std::numeric_limits<float>::digits);
std::cout << std::boolalpha << std::fixed;
float epsilon = std::numeric_limits<float>::epsilon();
float last = epsilon;
while (true) {
last = std::nextafter(last, -std::numeric_limits<float>::infinity());
if ((1.0f + last) > 1.0f) {
epsilon = last;
} else {
break;
}
}
// Does not satisfy condition
std::cout << "last: " << verify(last) << " " << last << std::endl;
// Satisfy condition
std::cout << "epsilon: " << verify(epsilon) << " " << epsilon << std::endl;
float half_epsilon = std::numeric_limits<float>::epsilon() / 2.0f;
float actual_epsilon = std::nextafter(half_epsilon, std::numeric_limits<float>::infinity());
// Same as 'last' at this point
std::cout << "half_epsilon: " << verify(half_epsilon) << " " << half_epsilon << std::endl;
// Same as 'epsilon' at this point
std::cout << "actual_epsilon: " << verify(actual_epsilon) << " " << actual_epsilon << std::endl;
return 0;
}
This outputs
last: false 0.000000059604644775390625
epsilon: true 0.000000059604651880817983
half_epsilon: false 0.000000059604644775390625
actual_epsilon: true 0.000000059604651880817983
https://coliru.stacked-crooked.com/a/3c66a2144e80a91b
Am I missing somethig here?
If I'm not wrong the definition of the Machine Epsilon is the lowest number that satisfies the condition: [
1 + epsilon > 1
]
Close, but you are wrong in the context of C++. (I believe your definition is correct in other, more academic contexts.) According to cppreference.com, the machine epsilon is "the difference between 1.0
and the next value representable by the [specified] floating-point type". The machine epsilon does satisfy 1 + epsilon > 1
, but it need not be the lowest number that satisfies that. It is, though, the lowest number that satisfies that condition under all rounding modes.
Because the machine epsilon is so much smaller than 1.0
, there are plenty of representable values between the epsilon and 0.0
. (That's a basic goal of floating point representations.) When any of these is added to 1.0
, the result is not representable, so the result needs to be rounded. If the rounding mode is to the nearest representable value, then that sum will round to 1 + epsilon
whenever the small number is between epsilon/2
and 3*epsilon/2
. On the other hand, if the rounding mode is always towards zero, then you get the result you were expecting.
Try adding #include <cfenv>
and the following line to your code.
fesetround(FE_TOWARDZERO);
This causes any sum strictly between 1.0
and 1 + epsilon
to round to 1.0
. You should now see the machine epsilon behaving as you expected.
The other guaranteed rounding modes are towards -infinity and towards +infinity. See cppreference.com for details.