计算1到2之间的数字的对数基数2的有效方法

时间:2017-08-03 22:29:40

标签: c performance optimization logarithm fixed-point

我正在开发一个定点平台(不支持浮点运算)。

我将任意有理数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;

2 个答案:

答案 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一旦部署

VOILÀ:

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: