超高速的反正切算法,纯整数运算

2019-12-10 18:25发布

本帖最后由 s1j2h3 于 2017-3-14 21:02 编辑

加入这个论坛,大部时间都在潜水,今天刚完成一个纯整数乘加运算的快速反正切算法,用于加速度传感器解析倾角。
大家评价下,看能否再优化

解析出的为放大了1600倍的角度值,右移四位就是精度为0.01度的角度值,范围为0~360度
编译完成的整个代码不到500字节,大家可以用IAR FOR STM8验证一下。


const static INT16U __tanz[] =
{
    32767        ,
    16384        ,
    8192        ,
    4096        ,
    2048        ,
    1024        ,
    512        ,
    256        ,
    128        ,
    64        ,
    32        ,
    16        ,
    8        ,
    4        ,
    2        ,
    1        ,
};

const static INT32U __angle[] =
{
    72000        ,
    42504        ,
    22458        ,
    11400        ,
    5722        ,
    2864        ,
    1432        ,
    716        ,
    358        ,
    179        ,
    90        ,
    45        ,
    22        ,
    11        ,
    6        ,
    3        ,
};


static INT32U __atan_core(INT16U tan)
{
    INT32S  x,y;
    INT32S  xt,yt,t;
    INT16U *ptan;
    INT32U *pang;
    INT32U  m,ang;         
   
    x = 65535;
    y = tan;
   
    ptan = (INT16U *)&__tanz;
    pang = (INT32U *)&__angle;
    m    =  0;
    for(INT8U i=0; i<16; i++)
    {
        t   = *ptan++;
        ang = *pang++;
        yt  = y*t;
        xt  = x*t;
        yt >>= 15;
        xt >>= 15;
        if(y>0)
        {
            x  += yt;
            y  -= xt;
            m  += ang;
        }
        else
        {
            x  -= yt;
            y  += xt;
            m  -= ang;
        }
    }
   
    return m;
}


INT32U __FastIntAtan2__(INT16S x, INT16S y)
{
    INT8U   flag = 0;
    INT32U  angle;
   
   
    if(x<0)
    {
        x = -x;
        flag |= 0x01;
    }
    if(y<0)
    {
        y = -y;
        flag |= 0x02;
    }
    if(y>x)
    {
        INT16S t;
        
        t = y;
        y = x;
        x = t;
        flag |= 0x04;
    }
   
    {
        INT32U m = y;
        
        m *= 65535;
        m /= (INT16U)x;
        angle = __atan_core(m);
    }
   
    if(flag & 0x04)
    {
        angle = 144000 - angle;  
    }
    if(flag & 0x01)
    {
        angle = 288000 - angle;   
    }
    if(flag & 0x02)
    {
        angle = 576000 - angle;
    }
   
    return angle;
}
友情提示: 此问题已得到解决,问题已经关闭,关闭后问题禁止继续编辑,回答。
该问题目前已经被作者或者管理员关闭, 无法添加新回复
99条回答
596142041
1楼-- · 2019-12-15 12:01
收藏了,多谢楼主分享.
vermon
2楼-- · 2019-12-15 12:02
正好弄得着,好思路
s1j2h3
3楼-- · 2019-12-15 16:59
xiaolaba 发表于 2017-3-15 18:14
以前看過最接近的寫法是這個
http://www.coranac.com/documents/arctangent/
但是還是不完全理解樓主寫的, ...

其实思路是一样的。不过我上面的代码只是稍微变化了点,更适合于我的应用而已。
而且,整数应用是没法做出通用版的。

最终精度是以0.1度,0.01度,还是0.001度为好?
应用不同,需要也不同。
mypc16888
4楼-- · 2019-12-15 21:25
非常不错的思路
xiaolaba
5楼-- · 2019-12-16 00:13
 精彩回答 2  元偷偷看……
s1j2h3
6楼-- · 2019-12-16 01:53
xiaolaba 发表于 2017-3-16 10:54
換個方向想

如果1600倍就是SCALAR, 那單位圓的半徑為1

这个,好像不行

预算好的正切值也需支持到相应的精度,否则光比例缩放是没有用的。
精度的大小也同样对应于查找的次数。
按你的想法,最好的就是提供足够的精度与足够的查找次数,而对于0.1度精度就能满足要求的需要求来说,你提供了0.001度的精度,那就多浪费了约7次的查找过程。
如果你不在乎这7次处理时间,那差不多也不在乎存储空间,那直接调用浮点库函数就行了

一周热门 更多>