似乎浮点表示形式的基数为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/