将`std :: floor()`和`std :: ceil()`转换为整数类型总能得到正确的结果吗?

hkB*_*sai 16 c++ math arithmetic-expressions floating-accuracy

我是偏执狂,其中一个函数可能会给出一个不正确的结果:

std::floor(2000.0 / 1000.0) --> std::floor(1.999999999999) --> 1
or
std::ceil(18 / 3) --> std::ceil(6.000000000001) --> 7
Run Code Online (Sandbox Code Playgroud)

可以这样的事情发生吗?如果确实存在这样的风险,我打算使用以下功能以便安全地工作.但是,这真的有必要吗?

constexpr long double EPSILON = 1e-10;

intmax_t GuaranteedFloor(const long double & Number)
{
    if (Number > 0)
    {
        return static_cast<intmax_t>(std::floor(Number) + EPSILON);
    }
    else
    {
        return static_cast<intmax_t>(std::floor(Number) - EPSILON);
    }
}

intmax_t GuaranteedCeil(const long double & Number)
{
    if (Number > 0)
    {
        return static_cast<intmax_t>(std::ceil(Number) + EPSILON);
    }
    else
    {
        return static_cast<intmax_t>(std::ceil(Number) - EPSILON);
    }
}
Run Code Online (Sandbox Code Playgroud)

(注意:我假设给定的'long double'参数适合'intmax_t'返回类型.)

Sne*_*tel 20

人们常常会得到浮点运算产生小的,不可预测的,准随机错误的结果的印象.这种印象不正确.

浮点计算尽可能精确.18/3将永远产生6.结果1/3不会恰好是三分之一,但它将是可以表示为浮点数的最接近三分之一的数字.

因此,您展示的示例始终有效.至于你建议的"保证楼/天花板",这不是一个好主意.某些操作序列可以很容易地将错误吹得远远超过上面1e-10,并且某些其他用例需要1e-10被正确地识别(和ceil'ed)为非零.

根据经验,硬编码的epsilon值是代码中的错误.