为什么 numeric_limits Machine Epsilon 不满足 1+e>1 条件?
Why the numeric_limits Machine Epsilon does not satisfy the 1+e>1 condition?
如果我没记错的话,Machine Epsilon 的定义是满足条件的最小数字:
我正在尝试使用 std::numeric_limits<float>::epsilon()
来测试它,但是如果您尝试使用 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;
}
此剧照输出true
https://coliru.stacked-crooked.com/a/841e19dafcf0bf6f.
在尝试使用 std::nextafter
获取数字后,我注意到正确的 Machine Epsilon 应该是:
std::nextafter(std::numeric_limits<float>::epsilon() / 2.0f, std::numeric_limits<float>::infinity())
我用这段代码测试了它:
#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;
}
这输出
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
我是不是遗漏了什么?
If I'm not wrong the definition of the Machine Epsilon is the lowest number that satisfies the condition: [1 + epsilon > 1
]
很接近,但是你在 C++ 的上下文中是错误的。 (我相信你的定义在其他更学术的背景下是正确的。)根据 cppreference.com,机器 epsilon 是“1.0
与 [指定] 浮点类型可表示的下一个值之间的差异”。机器 epsilon 确实满足 1 + epsilon > 1
,但它不必是满足该要求的 最低 数字。不过,它是满足该条件的最小数字 在所有舍入模式下。
因为机器 epsilon 比 1.0
小得多,所以在 epsilon 和 0.0
之间有很多可表示的值。 (这是浮点表示的基本目标。)当将这些中的任何一个添加到 1.0
时,结果不可表示,因此需要对结果进行舍入。如果四舍五入模式是最接近的可表示值,那么每当小数字在 epsilon/2
和 3*epsilon/2
之间时,该总和将四舍五入为 1 + epsilon
。另一方面,如果舍入模式始终趋向于零,那么您会得到预期的结果。
尝试将 #include <cfenv>
和以下行添加到您的代码中。
fesetround(FE_TOWARDZERO);
这会导致 1.0
和 1 + epsilon
之间的任何总和四舍五入为 1.0
。您现在应该看到机器 epsilon 的行为符合您的预期。
其他保证舍入模式是朝向-无穷大和朝向+无穷大。有关详细信息,请参阅 cppreference.com。
如果我没记错的话,Machine Epsilon 的定义是满足条件的最小数字:
我正在尝试使用 std::numeric_limits<float>::epsilon()
来测试它,但是如果您尝试使用 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;
}
此剧照输出true
https://coliru.stacked-crooked.com/a/841e19dafcf0bf6f.
在尝试使用 std::nextafter
获取数字后,我注意到正确的 Machine Epsilon 应该是:
std::nextafter(std::numeric_limits<float>::epsilon() / 2.0f, std::numeric_limits<float>::infinity())
我用这段代码测试了它:
#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;
}
这输出
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
我是不是遗漏了什么?
If I'm not wrong the definition of the Machine Epsilon is the lowest number that satisfies the condition: [
1 + epsilon > 1
]
很接近,但是你在 C++ 的上下文中是错误的。 (我相信你的定义在其他更学术的背景下是正确的。)根据 cppreference.com,机器 epsilon 是“1.0
与 [指定] 浮点类型可表示的下一个值之间的差异”。机器 epsilon 确实满足 1 + epsilon > 1
,但它不必是满足该要求的 最低 数字。不过,它是满足该条件的最小数字 在所有舍入模式下。
因为机器 epsilon 比 1.0
小得多,所以在 epsilon 和 0.0
之间有很多可表示的值。 (这是浮点表示的基本目标。)当将这些中的任何一个添加到 1.0
时,结果不可表示,因此需要对结果进行舍入。如果四舍五入模式是最接近的可表示值,那么每当小数字在 epsilon/2
和 3*epsilon/2
之间时,该总和将四舍五入为 1 + epsilon
。另一方面,如果舍入模式始终趋向于零,那么您会得到预期的结果。
尝试将 #include <cfenv>
和以下行添加到您的代码中。
fesetround(FE_TOWARDZERO);
这会导致 1.0
和 1 + epsilon
之间的任何总和四舍五入为 1.0
。您现在应该看到机器 epsilon 的行为符合您的预期。
其他保证舍入模式是朝向-无穷大和朝向+无穷大。有关详细信息,请参阅 cppreference.com。