【问题标题】:C++ next float with numeric_limits / epsilon?C ++下一个带有 numeric_limits / epsilon 的浮点数?
【发布时间】:2012-06-24 23:07:35
【问题描述】:

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

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

非常感谢。

【问题讨论】:

  • 你注意到 x prev next != x?
  • 你是说你不想要 (x+1) 而是增加尾数时的值?
  • 是的,我不想要 x+1 而是 x +/- epsilon(它是在考虑可能的精度问题的情况下检查边界)

标签: c++ floating-point limits epsilon


【解决方案1】:

以下 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) 是一个常数,因此可以预先计算。

【讨论】:

    【解决方案2】:

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

    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;
    }
    

    【讨论】:

    • 不适用于 -0(它给出 NaN,nextafterf 给出 1.4012984643e-45)。
    • @AProgrammer 我有一个小错误,移动了 30 而不是 31。它现在可以工作了吗?
    • 同样的问题。 -0 表示为 0x800000000 并且 next() 应该返回 0x00000001,我看不到避免对其进行测试的方法。 (然后我们将看看 qNaN 和 sNaN ;))
    • 绝妙的解决方案!我把它翻译成C#。多亏了 hack,它不仅是安全的代码,而且实际上比 C 代码更高效,不管你信不信。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      不,“连续”浮点值之间的比例不均匀;这种方法可能会遗漏一些内容,或者让您卡在xnext == x 的位置。

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

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

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

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

      【讨论】:

        猜你喜欢
        • 2021-10-16
        • 1970-01-01
        • 2021-10-27
        • 1970-01-01
        • 2012-04-26
        • 1970-01-01
        • 2012-08-06
        • 1970-01-01
        • 2010-10-09
        相关资源
        最近更新 更多