ldexp(1,x)和exp2(x)之间的区别

时间:2016-09-20 07:06:59

标签: c++ c++11 floating-point std cmath

似乎浮点表示具有基数2(即FLT_RADIX == 2),std::ldexp(1, x)std::exp2(x)2提升到给定的幂x

标准是否定义或提及它们之间的任何预期行为和/或性能差异?不同编译器的实践经验是什么?

1 个答案:

答案 0 :(得分:2)

exp2(x)ldexp(x,i)执行两项不同的操作。前者计算2 x ,其中x浮点数,而后者计算x * 2 i ,其中i整数。对于x的整数值,exp2(x)ldexp(1,int(x))将是等效的,前提是x到整数的转换不会溢出。

关于这两个功能的相对效率的问题没有明确的答案。它取决于硬件平台的功能和库实现的细节。虽然从概念上讲,ldexpf()看起来像是对浮点操作数的指数部分的简单操作,但实际上比一次更复杂,一旦通过非正规性考虑溢出和逐渐下溢。后一种情况涉及舍入浮点数的有效数(尾数)部分。

由于ldexp()通常是一种不经常使用的函数,根据我的经验相比,数学库编写者对其进行的优化工作较少,而不是其他数学函数。

在某些平台上,ldexp()或其更快(自定义)版本将用作exp2()软件实现中的构建块。以下代码为float参数提供了此方法的示例性实现:

#include <cmath>

/* Compute exponential base 2. Maximum ulp error = 0.86770 */
float my_exp2f (float a)
{
    const float cvt = 12582912.0f; // 0x1.8p23
    const float large = 1.70141184e38f; // 0x1.0p127
    float f, r;
    int i;

    // exp2(a) = exp2(i + f); i = rint (a)
    r = (a + cvt) - cvt;
    f = a - r;
    i = (int)r;
    // approximate exp2(f) on interval [-0.5,+0.5]
    r =             1.53720379e-4f;  // 0x1.426000p-13f
    r = fmaf (r, f, 1.33903872e-3f); // 0x1.5f055ep-10f
    r = fmaf (r, f, 9.61817801e-3f); // 0x1.3b2b20p-07f
    r = fmaf (r, f, 5.55036031e-2f); // 0x1.c6af7ep-05f
    r = fmaf (r, f, 2.40226522e-1f); // 0x1.ebfbe2p-03f
    r = fmaf (r, f, 6.93147182e-1f); // 0x1.62e430p-01f
    r = fmaf (r, f, 1.00000000e+0f); // 0x1.000000p+00f
    // exp2(a) = 2**i * exp2(f);
    r = ldexpf (r, i);
    if (!(fabsf (a) < 150.0f)) {
        r = a + a; // handle NaNs
        if (a < 0.0f) r = 0.0f;
        if (a > 0.0f) r = large * large; // + INF
    }
    return r;
}

exp2()的大多数实际实现不调用ldexp(),而是调用自定义版本,例如当支持整数和浮点数据之间的快速逐位传输时,此处由内部表示函数__float_as_int()__int_as_float()将IEEE-754 binary32重新解释为int32,反之亦然:

/* For a in [0.5, 4), compute a * 2**i, -250 < i < 250 */
float fast_ldexpf (float a, int i)
{
    int ia = (i << 23) + __float_as_int (a); // scale by 2**i
    a = __int_as_float (ia);
    if ((unsigned int)(i + 125) > 250) { // |i| > 125
        i = (i ^ (125 << 23)) - i; // ((i < 0) ? -125 : 125) << 23
        a = __int_as_float (ia - i); // scale by 2**(+/-125)
        a = a * __int_as_float ((127 << 23) + i); // scale by 2**(+/-(i%125))
    }
    return a;
}

在其他平台上,硬件提供exp2()的单精度版本作为快速硬件指令。在处理器内部,这些通常通过具有线性或二次插值的表查找来实现。在此类硬件平台上,ldexp(float)可以exp2(float)实现,例如:

float my_ldexpf (float x, int i)
{
    float r, fi, fh, fq, t;

    fi = (float)i;
    /* NaN, Inf, zero require argument pass-through per ISO standard */
    if (!(fabsf (x) <= 3.40282347e+38f) || (x == 0.0f) || (i == 0)) {
        r = x;
    } else if (abs (i) <= 126) {
        r = x * exp2f (fi);
    } else if (abs (i) <= 252) {
        fh = (float)(i / 2);
        r = x * exp2f (fh) * exp2f (fi - fh);
    } else {
        fq = (float)(i / 4);
        t = exp2f (fq);
        r = x * t * t * t * exp2f (fi - 3.0f * fq);
    }
    return r;

}

最后,有些平台基本上在硬件中提供exp2()ldexp()功能,例如x86处理器上的x87指令F2XM1FSCALE