我很偏执,其中一个函数可能会给出不正确的结果:
std::floor(2000.0 / 1000.0) --> std::floor(1.999999999999) --> 1
or
std::ceil(18 / 3) --> std::ceil(6.000000000001) --> 7
这样的事情会发生吗?如果确实存在这样的风险,我计划使用以下功能以便安全地工作。但是,这真的有必要吗?
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);
}
}
(注意:我假设给定的&#39; long double&#39;参数适合&#39; intmax_t&#39;返回类型。)
答案 0 :(得分:20)
人们常常会觉得浮点运算会产生小的,不可预测的,准随机错误的结果。这种印象不正确。
浮点计算尽可能精确。 18/3
将始终生成正好6 。 1/3
的结果不会恰好是三分之一,但它将是最接近三分之一的数字,可表示为浮点数。
因此,您展示的示例始终有效。至于你建议的“保证楼/天花板”,这不是一个好主意。某些操作序列很容易将错误推到1e-10
以上,而某些其他用例需要1e-10
才能正确识别(和ceil'ed)为非零。
根据经验,硬编码的epsilon值是代码中的错误。
答案 1 :(得分:4)
在您列出的具体示例中,我认为不会发生这些错误。
std::floor(2000.0 /*Exactly Representable in 32-bit or 64-bit Floating Point Numbers*/ / 1000.0 /*Also exactly representable*/) --> std::floor(2.0 /*Exactly Representable*/) --> 2
std::ceil(18 / 3 /*both treated as ints, might not even compile if ceil isn't properly overloaded....?*/) --> 6
std::ceil(18.0 /*Exactly Representable*/ / 3.0 /*Exactly Representable*/) --> 6
话虽如此,如果你的数学运算依赖于这些函数对浮点数的表现完全正确,这可能会说明你需要重新考虑/重新审视的设计缺陷。
答案 2 :(得分:1)
只要浮点值x和y精确地表示您使用的类型范围内的整数,就没有问题-x / y
总是会产生一个浮点值,该浮点值可以精确表示整数结果。始终将其强制转换为int即可。
但是,一旦浮点值超出类型(Representing integers in doubles)的整数可表示范围,则epsilons将无济于事。
请考虑以下示例。 16777217是不能完全表示为32位float
的最小整数:
int ix=16777217, iy=97;
printf("%d / %d = %d", ix, iy, ix/iy);
// yields "16777217 / 97 = 172961" which is accurate
float x=ix, y=iy;
printf("%f / %f = %f", x, y, x/y);
// yields "16777216.000000 / 97.000000 = 172960.989691"
在这种情况下,错误为负。在其他情况下(尝试16777219/1549),错误为正。
虽然很想添加一个epsilon来使floor
起作用,但是它并不会大大提高精度。 当值相差更大的数量级时,误差将变得大于1,并且不能保证整数精度。尤其是,当x/y
超过最大值时。可表示,错误可能超过1.0,所以epsilon毫无帮助。
如果这起作用了,您将不得不考虑改变数学方法-操作顺序,对数运算等。
答案 3 :(得分:-6)
使用双打时可能会出现这样的结果。你可以使用round或你可以减去0.5然后使用std :: ceil函数。