【问题标题】:Why the numeric_limits Machine Epsilon does not satisfy the 1+e>1 condition?为什么 numeric_limits Machine Epsilon 不满足 1+e>1 条件?
【发布时间】:2021-04-02 14:38:14
【问题描述】:

如果我没记错的话,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

我在这里错过了什么吗?

【问题讨论】:

标签: c++ floating-point precision floating-accuracy epsilon


【解决方案1】:

如果我没记错的话,Machine Epsilon 的定义是满足条件的最小数:[1 + epsilon &gt; 1]

关闭,但您在 C++ 上下文中是错误的。 (我相信您的定义在其他更学术的环境中是正确的。)根据cppreference.com,机器 epsilon 是“1.0 与 [指定] 浮点类型可表示的下一个值之间的差异”。机器 epsilon 确实满足 1 + epsilon &gt; 1,但它不必是满足该条件的 最低 数。但是,它是满足该条件的最小数字在所有舍入模式下

因为机器 epsilon 比 1.0 小得多,所以 epsilon 和 0.0 之间有很多可表示的值。 (这是浮点表示的基本目标。)当其中任何一个添加到1.0 时,结果是不可表示的,因此需要对结果进行四舍五入。如果舍入模式是最接近的可表示值,那么只要小数介于epsilon/23*epsilon/2 之间,总和就会舍入到1 + epsilon。另一方面,如果舍入模式总是趋近于零,那么您会得到您期望的结果。

尝试将#include &lt;cfenv&gt; 和以下行添加到您的代码中。

fesetround(FE_TOWARDZERO);

这会导致1.01 + epsilon 之间的任何和严格舍入为1.0。您现在应该会看到机器 epsilon 的行为与您预期的一样。

其他有保证的舍入模式是朝向 -infinity 和朝向 +infinity。详情请见cppreference.com

【讨论】:

    猜你喜欢
    • 2021-10-31
    • 1970-01-01
    • 2021-03-29
    • 1970-01-01
    • 2023-03-20
    • 1970-01-01
    • 1970-01-01
    • 2022-12-10
    • 2012-06-20
    相关资源
    最近更新 更多