为什么 numeric_limits Machine Epsilon 不满足 1+e>1 条件?

Why the numeric_limits Machine Epsilon does not satisfy the 1+e>1 condition?

提问人:Edoren 提问时间:12/24/2020 更新时间:12/24/2020 访问量:265

问:

如果我没记错的话,机器 Epsilon 的定义是满足条件的最小数字:

1+e>1

我试图利用 但该值不满足这一点,如果您尝试使用以下命令获取先前的浮点数: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;
}

这仍然输出 https://coliru.stacked-crooked.com/a/841e19dafcf0bf6ftrue

在尝试使用获取号码后,我注意到正确的机器 Epsilon 应该是:std::nextafter

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++ 浮点精度 epsilon

评论

0赞 1201ProgramAlarm 12/24/2020
stackoverflow.com/questions/28357415/......询问为什么计算 double 和 float epsilon 是相同的,为什么会这样。

答:

6赞 JaMiT 12/24/2020 #1

如果我没记错的话,机器 Epsilon 的定义是满足条件的最低数字:[1 + epsilon > 1]

接近,但在 C++ 的上下文中你错了。(我相信你的定义在其他更学术的背景下是正确的。根据 cppreference.com 的说法,机器 epsilon 是“[指定] 浮点类型可表示的下一个值之间的差值”。机器 epsilon 确实满足 ,但它不一定是满足该条件的最小数字。但是,它是在所有舍入模式下满足该条件的最低数字。1.01 + epsilon > 1

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

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

fesetround(FE_TOWARDZERO);

这会导致任何介于 和 之间的总和四舍五入为 。您现在应该看到计算机 epsilon 的行为符合您的预期。1.01 + epsilon1.0

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