似乎浮点表示形式的基数为2(即FLT_RADIX == 2),std::ldexp(1, x)std::exp2(x)都将2提高到给定的幂x

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

最佳答案

exp2(x)ldexp(x,i)执行两种不同的操作。前者计算2x,其中x是浮点数,而后者计算x * 2i,其中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()和将IEEE-754 __int_as_float()重新解释为binary32,反之亦然:

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


在其他平台上,硬件提供int32的单精度版本作为快速硬件指令。在处理器内部,这些通常通过具有线性或二次插值的表查找来实现。在这样的硬件平台上,可以根据exp2()来实现ldexp(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(float)exp2()功能,例如x86处理器上的x87指令ldexp()F2XM1

关于c++ - ldexp(1,x)和exp2(x)之间的差异,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/39587752/

10-09 03:15