如何使用exp2()的极小多项式逼近来实现2 ^ x定点算法s5.26,并且输入值在[-31.9,31.9]范围内 如何使用以下链接中提到的Sollya工具生成多项式 Power of 2 approximation in fixed point
答案 0 :(得分:2)
由于定点算术通常不包含表示溢出结果的“无穷大”编码,因此exp2()
格式的s5.26
的任何实现都将限于间隔(-32,5 ),则输出为[0,32)。
超越函数的计算通常包括参数减少,核心近似和最终结果构造。在exp2(a)
的情况下,合理的参数减少方案是将a
分成整数部分i
和小数部分f
,使得a == i + f
与{ [-0.5,0.5]中的{1}}。然后计算f
,并将结果缩放2 exp2(f)
,这与定点算术i
的移位相对应。
计算exp2(a) = exp2(f) * exp2(i)
的常见设计选择是在exp2(f)
的表格值中进行插值或多项式逼近。由于我们需要31个结果位来容纳最大的参数,因此精确插值可能要使用二次插值来保持表格大小合理。由于许多现代处理器(包括嵌入式系统中使用的处理器)提供了快速的整数乘法器,因此在这里我将重点介绍多项式的近似。为此,我们需要一种具有minimax属性的多项式,即与参考相比具有最小化最大误差的多项式。
商业工具和免费工具都提供内置功能来生成minimax近似值,例如Mathematica的exp2()
命令,Maple的MiniMaxApproximation
命令和Sollya的minimax
命令。人们可能还会选择基于Remez algorithm构建自己的基础架构,这是我使用的方法。与通常使用最接近或偶数舍入的浮点运算相反,定点运算通常限于中间结果的截断。这会在表达式求值期间添加其他错误。因此,通常最好尝试进行启发式搜索,以对生成的近似值的系数进行小的调整,以部分平衡那些累积单边误差的情况。
由于结果中最多需要31位,并且由于核心近似中的系数通常在大小上小于1,因此我们无法使用本机定点精度(此处为fpminimax
)进行多项式求值。相反,我们希望通过动态调整工作中的定点格式来扩大中间计算中的操作数,以充分利用32位整数的可用范围。出于效率的考虑,建议安排这样的计算,以便乘法使用重新归一化右移32位。这通常可以消除32位处理器上的显式移位。
由于中间计算使用带符号的数据,因此将发生带符号的负操作数的右移。我们希望这些右移映射到算术右移指令,这是C标准不不能保证的。但是在最常用的平台上,C编译器可以满足我们的要求。否则,可能必须求助于内在函数或内联汇编。我在x64平台上使用Microsoft编译器开发了以下代码。
在评估s5.26
的多项式逼近时,原始浮点系数,动态缩放和启发式调整都清晰可见。下面的代码不能完全实现大型参数的完全准确性。最大的绝对误差是1.10233e-7,因为参数exp2(f)
= 4.71739332:0x12de9c5b
返回fixed_exp2()
,而准确的结果将是0x693ab6a3
。可以通过将多项式核逼近度提高一级来实现完全精度。
0x693ab69c
基于表的替代方案将权衡表存储空间,以减少执行的计算量。根据L1数据缓存的大小,这可能会或可能不会提高性能。一种可能的方法是将[0,1)中#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <math.h>
/* on 32-bit architectures, there is often an instruction/intrinsic for this */
int32_t mulhi (int32_t a, int32_t b)
{
return (int32_t)(((int64_t)a * (int64_t)b) >> 32);
}
/* compute exp2(a) in s5.26 fixed-point arithmetic */
int32_t fixed_exp2 (int32_t a)
{
int32_t i, f, r, s;
/* split a = i + f, such that f in [-0.5, 0.5] */
i = (a + 0x2000000) & ~0x3ffffff; // 0.5
f = a - i;
s = ((5 << 26) - i) >> 26;
f = f << 5; /* scale up for maximum accuracy in intermediate computation */
/* approximate exp2(f)-1 for f in [-0.5, 0.5] */
r = (int32_t)(1.53303146e-4 * (1LL << 36) + 996);
r = mulhi (r, f) + (int32_t)(1.33887795e-3 * (1LL << 35) + 99);
r = mulhi (r, f) + (int32_t)(9.61833261e-3 * (1LL << 34) + 121);
r = mulhi (r, f) + (int32_t)(5.55036329e-2 * (1LL << 33) + 51);
r = mulhi (r, f) + (int32_t)(2.40226507e-1 * (1LL << 32) + 8);
r = mulhi (r, f) + (int32_t)(6.93147182e-1 * (1LL << 31) + 5);
r = mulhi (r, f);
/* add 1, scale based on integral portion of argument, round the result */
r = ((((uint32_t)r * 2) + (uint32_t)(1.0*(1LL << 31)) + ((1U << s) / 2) + 1) >> s);
/* when argument < -26.5, result underflows to zero */
if (a < -0x6a000000) r = 0;
return r;
}
/* convert from s5.26 fixed point to double-precision floating point */
double fixed_to_float (int32_t a)
{
return a / 67108864.0;
}
int main (void)
{
double a, res, ref, err, maxerr = 0.0;
int32_t x, start, end;
start = -0x7fffffff; // -31.999999985
end = 0x14000000; // 5.000000000
printf ("testing fixed_exp2 with inputs in [%.9f, %.9f)\n",
fixed_to_float (start), fixed_to_float (end));
for (x = start; x < end; x++) {
a = fixed_to_float (x);
ref = exp2 (a);
res = fixed_to_float (fixed_exp2 (x));
err = fabs (res - ref);
if (err > maxerr) {
maxerr = err;
}
}
printf ("max. abs. err = %g\n", maxerr);
return EXIT_SUCCESS;
}
的2 f -1制成表格。将函数参数拆分为整数f
和小数i
,以使f
在[0,1)中。为了使表格保持较小,请使用二次插值,并从三个连续的表格条目中动态计算多项式的系数。通过启发式确定的偏移量对结果进行微调,以在一定程度上补偿定点算法的截断性质。
该表由分数f
的前导位索引。使用七个位作为索引(导致有128 + 2个条目的表),准确性比以前的minimax多项式近似略差。最大绝对误差为1.74935e-7。对于参数f
= 4.33593750,它会发生,其中0x11580000
返回fixed_exp2()
,而准确的结果将是0x50c7d771
。
0x50c7d765