为了开始使用CORDIC for log10,我实现了在this PDF, pp6中派生的算法:

#include <stdio.h>
#include <math.h>

// https://www.mikrocontroller.net/attachment/31117/cordic1.pdf
float logf_cordic (float x, int B)
{
    float z = 0.0f;

    for (int i = 1; i <= B; i++)
    {
        if (x > 1.0f)
        {
            printf ("-");
            x = x - ldexpf (x, -i);
            z = z - log10f (1.0f - ldexpf (1.0f, -i));
        }
        else
        {
            printf ("+");
            x = x + ldexpf (x, -i);
            z = z - log10f (1.0f + ldexpf (1.0f, -i));
        }
    }
    return z;
}

这是本文中代码的直接C99实现,其中我使用ldexpf(x,n)来计算x·2n.文中称,该方法对0.4194 < x < 3.4627收敛.该程序使用1 ≤ x ≤ 2.打印以下完整的C99代码:

+--+--+--+-+-+++--++ x = 1.00000: y = -0.000000, dy = -1.487272e-07
-+++++++++++++++++++ x = 1.12500: y = +0.099773, dy = +4.862081e-02
-+++++++++++++++++++ x = 1.25000: y = +0.099773, dy = +2.863325e-03
-+++-+--+-+--+-++--+ x = 1.37500: y = +0.138302, dy = -4.023314e-07
-++-+--++----+++--+- x = 1.50000: y = +0.176091, dy = -2.831221e-07
-+-+++++-++-++-++++- x = 1.62500: y = +0.210854, dy = +2.831221e-07
-+-+-+-+++++--+---++ x = 1.75000: y = +0.243038, dy = +2.235174e-07
-+--++-+--+---+---+- x = 1.87500: y = +0.273001, dy = +0.000000e+00
-+---+--+++--------- x = 2.00000: y = +0.301030, dy = -5.960464e-08

因此,它的工作与预期的一样,除了x = 1.125, 1.25,在那里误差很大,并且在计算更多迭代时不会减少.我现在盯着那个代码看了几个小时,但找不到我错过的东西……


完整的C99代码

#include <stdio.h>
#include <math.h>

float logf_cordic (float x, int B)
{
    float z = 0.0f;

    for (int i = 1; i <= B; i++)
    {
        if (x > 1.0f)
        {
            printf ("-");
            x = x - ldexpf (x, -i);
            z = z - log10f (1.0f - ldexpf (1.0f, -i));
        }
        else
        {
            printf ("+");
            x = x + ldexpf (x, -i);
            z = z - log10f (1.0f + ldexpf (1.0f, -i));
        }
    }
    return z;
}

int main (int argc, char *argv[])
{
    int ex = 3;
    int B = 20;

    if (argc >= 2)
        sscanf (argv[1], "%i", &ex);

    if (argc >= 3)
        sscanf (argv[2], "%i", &B);

    if (ex < 0) ex = 0;
    if (ex > 16) ex = 16;
    if (B > 100) B = 100;

    int n = 1 << ex;
    float dx = 1.0f / n;
    for (int i = 0; i <= n; ++i)
    {
        float x = 1.0f + i * dx;
        float y = logf_cordic (x, B);
        float dy = y - log10f (x);
        printf (" x = %.5f: y = %+f, dy = %+e\n",
                (double) x, (double) y, (double) dy);
    }
    return 0;
}

以下是本文介绍的算法,以供参考:

log10(x){
    z = 0;
    for ( i=1;i=<B;i++ ){
        if (x > 1)
            x = x - x*2^(-i);
            z = z - log10(1-2^(-i));
        else
            x = x + x*2^(-i);
            z = z - log10(1+2^(-i));
    }
return(z)
}

推荐答案

这个算法是假的.问题是,并不是所有的数字都可以用以下形式表示

X=Π(1±2-k)

看看这个discussion on MSE. 甚至不存在区间[a,b],使得该区间中的所有x都可以用这种形式表示.


然而,does exist是形式的表示

X=[[(1+ak2)

对于所有1≤x≤2.38=x0,在{0,1}中有k,这足以修复算法:

要计算LOG(X),首先将参数x归一化,以便 1/x0≤x≤1.

然后将以下算法应用于减少的x:

logB_cordic (x, N)
    z = 0
    xmax = 1 + 2^{−N−1}
    FOR k = 1 ... N
        xk = x + x·2^{-k}
        IF xk ≤ xmax
            x = xk;
            z = z - logB (1 + 2^{−k})
    return z

实际上,N是固定的,N logB值来自查找表.除此之外,该方法还需要通过变量偏移量进行加法、比较和移位.

比较IF xk ≤ xmax使最终绝对误差(几乎)对称在0附近.对于IF xk ≤ 1,绝对误差将是不对称的,并且高出一倍.

结果

For reference, below is an implementation in C99 that adds some confidence that the fixed algorithm is actually working. It's B = 2, N = 10, and the plot is for 16385 values of x evenly spaced over [0.5, 1].
(click to enlarge)

Absolute error of a C99 implementation with N=10

C99 Code

#include <math.h>

double log2_cordic (double x, int N)
{
    double xmax = 1.0 + ldexp (1.0, -N - 1);
    double z = 0.0;

    for (int k = 1; k <= N; k++)
    {
        double x_bk = x + ldexp (x, -k);

        if (x_bk <= xmax)
        {
            x = x_bk;
            z = z - log2 (1.0 + ldexp (1.0, -k));
        }
    }
    return z;
}

编辑:

刚发现我重新发明了轮子...根据Wikipedia的说法,这是费曼在曼哈顿项目中已经使用的方法.

C++相关问答推荐

C限制限定符是否可以通过指针传递?

C sscanf没有捕获第二个参数

当包含头文件时,gcc会发出隐式函数声明警告

为什么listen()(在调用accept()之前)足以让应用程序完成3次握手?

C中空终止符后面的数字?

为什么在C中二维字符数组会有这样的行为?

使用双指针动态分配和初始化2D数组

在我的代码中,我需要在哪里编写输出函数?

错误:包含文件时类型名称未知

如何在STM8项目中导入STM8S/A标准外设库(ST VisualDeveloper)?

GTK3按钮信号错误

在C中包装两个数组?

在进程之间重定向输出和输入流的问题

这个空指针类型的转换是有效代码还是恶意代码?

为什么我在C代码中得到一个不完整的类型?

在C程序中使用Beaglebone Black UART的问题

我可以创建适用于不同endian的 colored颜色 struct 吗?

如何使用 VLA 语法使用 const 指针声明函数

与 C 相比,C++ 中无副作用的无限循环的好处是 UB?

文件指针引起的C程序分段错误