我正在开发一个定点平台(不支持浮点运算)。
我将任意有理数q
表示为q * (1 << precision)
的最低值。
我需要一种有效的方法来计算x
的日志库2,其中1 < x < 2
。
这是我到目前为止所做的:
uint64_t Log2(uint64_t x, uint8_t precision)
{
uint64 res = 0;
uint64 one = (uint64_t)1 << precision;
uint64 two = (uint64_t)2 << precision;
for (uint8_t i = precision; i > 0 ; i--)
{
x = (x * x) / one; // now 1 < x < 4
if (x >= two)
{
x >>= 1; // now 1 < x < 2
res += (uint64_t)1 << (i - 1);
}
}
return res;
}
这很好用,但它会影响我的程序的整体性能,这需要为大量的输入值执行此操作。
尽管如此,使用的precision
是31,但这可能会改变,因此我需要将其保留为变量。
我可以在这里应用任何优化吗?
我想的是“先乘,最后总结”的形式。
但这意味着计算x ^ (2 ^ precision)
,这会很快溢出。
谢谢。
更新
我以前试图摆脱分支,但它只会让事情变得更糟:
for (uint8_t i = precision; i > 0 ; i--)
{
x = (x * x) / one; // now 1 < x < 4
uint64_t n = x / two;
x >>= n; // now 1 < x < 2
res += n << (i - 1);
}
return res;
答案 0 :(得分:1)
我唯一能想到的就是用右移而不是递减来循环,并将一些操作改为等效的二进制操作。这可能与您的平台相关,也可能不相关,但在我的x64 PC中,它们的性能提高了约2%:
uint64_t Log2(uint64_t x, uint8_t precision)
{
uint64_t res = 0;
uint64_t two = (uint64_t)2 << precision;
for (uint64_t b = (uint64_t)1 << (precision - 1); b; b >>= 1)
{
x = (x * x) >> precision; // now 1 < x < 4
if (x & two)
{
x >>= 1; // now 1 < x < 2
res |= b;
}
}
return res;
}
答案 1 :(得分:1)
我的建议将从相反的方向发展 - 在固定步数下使用恒定性能。
如果合理的少量资源仍然足够并且精确目标已知且始终达到,则恒定性能部署可以胜过大多数迭代方案。
log2(x)的泰勒展开(自1715年起)提供了一个实体微积分基础加上(几乎)无限精度a-priori已知可用于任何深度的定点算术(对于Epiphany / FPGA / ASIC /你把它保密了......)
Math将整个问题转换为可选的少量几个节点X_tab_i
,其中(只需平台精度要求)常量为pre-calculated for each node point。其余的是泰勒和产品的平台有效组合,在恒定时间+在设计驱动阈值下具有残余误差获得结果(目标PSPACE x PTIME约束权衡在设计阶段是显而易见的,但流程始终是CTIME,CSPACE一旦部署)
Given X: lookup closest X_tab_i,
with C0_tab_i, C1_tab_i, C2_tab_i, .., Cn_tab_i
//-----------------------------------------------------------------<STATIC/CONST>
// ![i]
#DEFINE C0_tab_i <log2( X_tab_i )>
#DEFINE C1_tab_i < ( X_tab_i )^(-1) * ( +1 / ( 1 * ln(2) )>
#DEFINE C2_tab_i < ( X_tab_i )^(-2) * ( -1 / ( 2 * ln(2) )>
#DEFINE C3_tab_i < ( X_tab_i )^(-3) * ( +1 / ( 3 * ln(2) )>
::: : : :
#DEFINE CN_tab_i < ( X_tab_i )^(-N) * ( -1^(N-1) ) / ( N * ln(2) )>
// -----------------------------------------------------------------<PROCESS>-BEG
DIFF = X - X_tab_i; CORR = DIFF;
RES = C0_tab_i
+ C1_tab_i * CORR; CORR *= DIFF;
RES += C2_tab_i * CORR; CORR *= DIFF;
... +=
RES += Cn_tab_i * CORR; CORR *= DIFF;
// --------------------------------------------------------------<PROCESS>-END: