5

考虑 C++ 中的“正常”实数TREAL x(不是次正常的,也不是 NaN/Infinite)(TREAL= float, double, long double)从浮点的角度来看,
以下是找到前一个和下一个的好解决方案吗?x

TREAL xprev = (((TREAL)(1.)) - std::numeric_limits<TREAL>::epsilon()) * x;
TREAL xnext = (((TREAL)(1.)) + std::numeric_limits<TREAL>::epsilon()) * x;

非常感谢你。

4

4 回答 4

13

<math.h>C99 和 C++11 在和中具有 nextafter、nextafterl 和 nextafterf 函数<cmath>。用基本的算术和 epsilon 实现它们会很乏味,因为您需要考虑四舍五入。处理二进制表示可能更容易,但我想知道符号和幅度表示的影响以及 -0.0 的存在(有关需要什么,请参阅Fred 的回答)。

于 2012-06-22T16:14:42.613 回答
5

在二进制级别上获取下一个浮点数要容易得多:

float next(float f)
{
    unsigned x;
    memcpy(&x, &f, 4);
    ++x;
    memcpy(&f, &x, 4);
    return f;
}

当然,这仅适用于浮点数“按升序”存储的系统,IEEE754 恰好就是这种情况。

负数将趋向负无穷大。想让它们归零吗?用这个:

float next(float f)
{
    int x;
    memcpy(&x, &f, 4);
    x += x >> 31 | 1;   // this will add 1 for positive f and -1 for negative f
    memcpy(&f, &x, 4);
    return f;
}
于 2012-06-22T16:17:24.303 回答
2

不,“连续”浮点值之间的比例不统一;这种方法可能会遗漏一些内容,或者让您陷入困境xnext == x

要从一个值移动到下一个最大值,您必须:

  • 提取尾数和指数;
  • 增加尾数;
  • 如果溢出,重置它并增加指数;
  • 从指数和尾数重建值。

细节非常繁琐,可能需要一些浮点表示的知识。

但是,假设一个类似于 IEEE 的表示,您可以通过将位模式重新解释为一个足够大的整数并增加该整数来实现这一点。这将增加尾数,任何溢出都会进入指数,就像我们想要的那样。

于 2012-06-22T16:17:55.347 回答
0

以下 nextFloat 函数为至少 minV 的所有浮点数(即 nextFloat 中的中间值保持在非正规范围之外的浮点数)给出正确的结果。我测试了从 minV 到 FLT_MAX 的所有浮点数,结果始终等于 nextFloatRef 的结果。

float nextFloatRef(float v)
{
    uint32_t vBits = reinterpret_cast<uint32_t&>(v);
    vBits++;
    return reinterpret_cast<float&>(vBits);
}

float nextFloat(float v)
{
    return v + v * nextFloatRef(FLT_EPSILON / 2);
}

float minV = FLT_MIN / (FLT_EPSILON / 2);

nextFloatRef(FLT_EPSILON / 2) 是一个常数,因此可以预先计算。

于 2018-04-03T16:45:47.860 回答