代码之家  ›  专栏  ›  技术社区  ›  pgast

在标准化浮点数f之后(之前),下一个标准化浮点数是什么?

  •  6
  • pgast  · 技术社区  · 15 年前

    给定一个标准化的浮点数f,下一个是什么 归一化 F之后/之前的浮点数。

    通过位旋转、提取尾数和指数,我有:

    next_normalized(double&){
          if mantissa is not all ones
              maximally denormalize while maintaining equality 
              add 1 to mantissa
              normalize
          else 
              check overflow
              set mantissa to 1  
              add (mantissa size in bits) to exponent.
          endif
     }
    

    但与其这样做,还不如用浮点运算来完成它?

    AS

    std::numeric_limits<double>::epsilon() 
    

    只是“邻域”为1时的误差差。-例如:

    normalized(d+=std::numeric_limits<double>::epsilon()) = d for d large
    

    它看起来更像是一个误差比,而不是一个误差差,因此我天真的直觉是

    (1.+std::numeric_limits<double>::epsilon())*f //should be the next.
    

    (1.-std::numeric_limits<double>::epsilon())*f //should be the previous.
    

    特别是,我有3个问题:是否有人做了以下任何一项(对于IEEE754):

    1)是否对此问题进行了错误分析?

    2)证明(或能够证明)任何 归一化 双D

        (1.+std::numeric_limits<double>::epsilon())*d != d ?
    

    3)证明 归一化 双数d不存在双f

        d < f < (1.+std::numeric_limits<double>::epsilon())*d ? 
    
    5 回复  |  直到 7 年前
        1
  •  7
  •   Robert Kern    15 年前

    我不知道你说的是什么意思 归一化 两倍的号码,但下一个 可表示的 双编号完成 nextafter() function 在大多数C标准数学库中。

        2
  •  5
  •   Stephen Canon    7 年前

    正如robert kern所指出的,您需要c nextafter()函数,或者ieee754 nextup()和nextdown()函数,尽管这两个函数还没有广泛实现。

    如果您出于某种原因想要避免下一个员工,您可以这样做:

    double next = x + scalbn(1.0, ilogb(x) - 52);
    

    这会将2^(x-52的指数)加到x上,x在最后一个位置(ulp)正好是一个单位。

    如果没有可用的常规cmath函数:

    double x = 1.0;
    uint64_t rep;
    assert(sizeof x == sizeof rep);
    memcpy(&rep, &x, sizeof x);
    rep += 1;
    memcpy(&x, &rep, sizeof x);
    

    这将通过对浮点值的位表示进行操作,将一个值添加到x的有效位;如果下一个值位于下一个二进制代码中,则这将进入指数,返回正确的值。如果你希望它对负值有效,你需要调整它。

        3
  •  3
  •   Jitse Niesen    15 年前

    3)项下的陈述是错误的。如果d略小于2,则d和(1+eps)*d之间正好有1个浮点数。下面是一个程序来显示它:

    #include <limits>
    #include <iostream>
    
    int main(int, char**)
    {
      using namespace std;
      double d = 1.875;
      cout.precision(18);
      cout << "d = " << d << "\n";
      double d2 = (1.+numeric_limits<double>::epsilon())*d;
      cout << "d2 = " << d2 << "\n";
      double f = d + (d2-d)/2;
      cout << "f = " << f << "\n";
    }
    

    原因是(1+eps)*1.875等于1.875+1.875*eps,四舍五入为1.875+2*eps。但是,1和2之间的连续浮点数的差是eps,所以1.875和1.875+2*eps之间有一个浮点数,即1.875+eps。

    我认为2)项下的陈述是正确的。罗伯特·克恩可能回答了你真正的问题。

        4
  •  1
  •   pgast    15 年前

    如下面所述,经过一小部分的研究,发现对于英特尔IEEE754格式的n位正浮点数,<+无穷大将连接的指数和有效位视为n-1位无符号整数,加上一个得到下一个更高的值(减去一个得到下一个更低的值)。

    反之亦然。特别地,我们可以将n-1位整数解释为表示与符号无关的绝对量值。因此,当负浮点数f之后,必须减去1才能使下一个浮点数接近零。

        5
  •  0
  •   aka.nice    12 年前

    1.0-epsilon不是1.0的前身,因此负计数器部分根本不起作用…
    1.0的前身是1.0-epsilon/2.0