似乎浮点表示具有基数2(即FLT_RADIX == 2
),std::ldexp(1, x)
和std::exp2(x)
将2
提升到给定的幂x
。
标准是否定义或提及它们之间的任何预期行为和/或性能差异?不同编译器的实践经验是什么?
答案 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指令F2XM1
和FSCALE
。