【问题标题】:Fast approximate float division快速近似浮点除法
【发布时间】:2015-06-24 15:50:45
【问题描述】:

在现代处理器上,浮点除法比浮点乘法慢一个数量级(以倒数吞吐量衡量)。

我想知道是否有任何算法可以计算x/y 的快速近似值,给定某些假设和容差水平。例如,如果您假设0<x<y,并且愿意接受任何在真值 10% 以内的输出,那么是否有比内置 FDIV 操作更快的算法?

【问题讨论】:

  • 您是否考虑过改变算法的结构以避免除法,而是尝试找到更快的除法技术?
  • 每除法 10% 的误差在重用时会导致指数级误差,值得吗?可能不会
  • 这纯粹是为了求知欲,没有计划的应用。
  • x * 1/y 如果可以预先计算倒数,则更快。
  • this 的重复项?嗯,我不知道它是否更快。有点意思。

标签: c++ division


【解决方案1】:

我希望这会有所帮助,因为这可能与您要查找的内容一样接近。

__inline__ double __attribute__((const)) divide( double y, double x ) {
                                    // calculates y/x
    union {
        double dbl;
        unsigned long long ull;
    } u;
    u.dbl = x;                      // x = x
    u.ull = ( 0xbfcdd6a18f6a6f52ULL - u.ull ) >> (unsigned char)1;
                                    // pow( x, -0.5 )
    u.dbl *= u.dbl;                 // pow( pow(x,-0.5), 2 ) = pow( x, -1 ) = 1.0/x
    return u.dbl * y;               // (1.0/x) * y = y/x
}


另见:
Another post about reciprocal approximation.
The Wikipedia page.

【讨论】:

    【解决方案2】:

    FDIV 通常异常比 FMUL 慢,只是 b/c 它不能像乘法一样通过管道传输,并且需要多个 clk 周期来进行迭代收敛硬件搜索过程。

    最简单的方法是简单地认识到除法只不过是被除数y 和除数的倒数x 的乘积。不那么直接的部分是记住浮点值x = m * 2 ^ e 及其倒数x^-1 = (1/m)*2^(-e) = (2/m)*2^(-e-1) = p * 2^q 近似于这个新的尾数p = 2/m = 3-x, for 1<=m<2。这给出了反函数的粗略分段线性逼近,但是我们可以通过使用迭代牛顿求根方法来改进该逼近,从而做得更好。

    w = f(x) = 1/x,这个函数f(x) 的逆函数是通过根据wx = f^(-1)(w) = 1/w 求解x 得到的。为了提高求根方法的输出,我们必须首先创建一个函数,其零反映了所需的输出,即g(w) = 1/w - x, d/dw(g(w)) = -1/w^2

    w[n+1]= w[n] - g(w[n])/g'(w[n]) = w[n] + w[n]^2 * (1/w[n] - x) = w[n] * (2 - x*w[n])

    w[n+1] = w[n] * (2 - x*w[n]), when w[n]=1/x, w[n+1]=1/x*(2-x*1/x)=1/x

    然后将这些组件相加得到最后一段代码:

    float inv_fast(float x) {
        union { float f; int i; } v;
        float w, sx;
        int m;
    
        sx = (x < 0) ? -1:1;
        x = sx * x;
    
        v.i = (int)(0x7EF127EA - *(uint32_t *)&x);
        w = x * v.f;
    
        // Efficient Iterative Approximation Improvement in horner polynomial form.
        v.f = v.f * (2 - w);     // Single iteration, Err = -3.36e-3 * 2^(-flr(log2(x)))
        // v.f = v.f * ( 4 + w * (-6 + w * (4 - w)));  // Second iteration, Err = -1.13e-5 * 2^(-flr(log2(x)))
        // v.f = v.f * (8 + w * (-28 + w * (56 + w * (-70 + w *(56 + w * (-28 + w * (8 - w)))))));  // Third Iteration, Err = +-6.8e-8 *  2^(-flr(log2(x)))
    
        return v.f * sx;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-15
      • 1970-01-01
      • 2015-05-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多