为什么 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;
}

此剧照输出truehttps://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/23*epsilon/2 之间时,该总和将四舍五入为 1 + epsilon。另一方面,如果舍入模式始终趋向于零,那么您会得到预期的结果。

尝试将 #include <cfenv> 和以下行添加到您的代码中。

fesetround(FE_TOWARDZERO);

这会导致 1.01 + epsilon 之间的任何总和四舍五入为 1.0。您现在应该看到机器 epsilon 的行为符合您的预期。

其他保证舍入模式是朝向-无穷大和朝向+无穷大。有关详细信息,请参阅 cppreference.com