Math 不动点的逆sqrt

Math 不动点的逆sqrt,math,fixed-point,inverse,sqrt,Math,Fixed Point,Inverse,Sqrt,我正在寻找固定点16.16数字的最佳平方根逆算法。下面的代码是我到目前为止的代码(但基本上它取平方根并除以原始数字,我想得到不带除法的平方根倒数)。如果它改变了什么,代码将为armv5te编译 uint32_t INVSQRT(uint32_t n) { uint64_t op, res, one; op = ((uint64_t)n<<16); res = 0; one = (uint64_t)1 << 46; while (on

我正在寻找固定点16.16数字的最佳平方根逆算法。下面的代码是我到目前为止的代码(但基本上它取平方根并除以原始数字,我想得到不带除法的平方根倒数)。如果它改变了什么,代码将为armv5te编译

uint32_t INVSQRT(uint32_t n)
{
    uint64_t op, res, one;
    op = ((uint64_t)n<<16);
    res = 0;
    one = (uint64_t)1 << 46;
    while (one > op) one >>= 2;
    while (one != 0)
    {
        if (op >= res + one)
        {
            op -= (res + one);
            res +=  (one<<1);
        }
        res >>= 1;
        one >>= 2;
    }
    res<<=16;
    res /= n;
    return(res);
}
uint32\u t INVSQRT(uint32\u t n)
{
uint64_t op,res,1;
op=((uint64_t)n=2;
而(一!=0)
{
如果(op>=res+one)
{
op-=(res+1);
res+=(一个=1;
一个>>=2;
}

res诀窍是将牛顿方法应用于问题x-1/y^2=0。因此,给定x,使用迭代格式求解y

Y_(n+1) = y_n * (3 - x*y_n^2)/2
除以2只是一个位移位,或者最坏的情况是乘以0.5。该方案完全按照要求收敛到y=1/sqrt(x),并且根本没有任何真正的除法


唯一的问题是您需要一个合适的y起始值。我记得,迭代收敛的估计值y是有限制的。

ARMv5TE处理器提供了一个快速整数乘法器和一个“计数前导零”指令。它们通常还带有中等大小的缓存。基于此,高性能实现的最合适方法似乎是查找表以获得初始近似值,然后进行两次Newton-Raphson迭代以获得完全准确的结果。我们可以通过添加被纳入表格的预计算,克雷计算机四十年前使用的一种技术

下面的函数
fxrsqrt()
实现了这种方法。它首先对参数
a
的倒数平方根进行8位近似
r
,但不是存储
r
,而是每个表元素存储3r(在32位条目的下10位)和r3(在32位条目的上22位)。这允许快速计算第一次迭代,如下所示: r1=0.5*(3*r-a*r3)。然后以常规方式计算第二次迭代,即r2=0.5*r1*(3-r1*(r1*a))

为了能够准确地执行这些计算,无论输入的大小,参数
a
在计算开始时被标准化,本质上表示为
2.32
定点数乘以2 scal的比例因子。在计算结束时,根据公式1/sqrt(22n)=2-n。通过对最重要的丢弃位为1的结果进行四舍五入,提高了精度,导致几乎所有结果都正确四舍五入。详尽的测试报告:
结果过低:639过高:1454未正确四舍五入:2093

该代码使用了两个辅助函数:
\uuuuuclz()
确定非零32位参数中前导零位的数量。
\uuuumulhi()
计算两个无符号32位整数的完整64位乘积的32个最高有效位。这两个函数都应该通过编译器内部函数或使用一位内联汇编来实现。在下面的代码中,我展示了非常适合ARM CPU的可移植实现以及x86平台的内联汇编版本。OnARMv5TE平台
\uuuu clz()
应映射到
clz
指令,而
\uuuuuumulhi()
应映射到
UMULL

#包括
#包括
#包括
#包括
#定义使用自己的内部函数1
#如果使用自己的内部函数
__forceinline int_uuuCLZ(uint32_uT a)
{
INTR;
__asm__u(“bsrl%1,%0\n\t):“=r”(r):“r”(a));
返回31-r;
}
uint32_t_uu umulhi(uint32_t a,uint32_t b)
{
uint32_t r;
__asm(移动%1%%eax\n\t全部%2\n\tmovl%%edx,%0\n\t)
:“=r”(r):“r”(a),“r”(b):“eax”,“edx”);
返回r;
}
#否则//使用自己的内在
内部clz(uint32\U t a)
{
uint32_t r=32;
如果(a>=0x00010000){a>=16;r-=16;}
如果(a>=0x00000100){a>=8;r-=8;}
如果(a>=0x00000010){a>=4;r-=4;}
如果(a>=0x00000004){a>=2;r-=2;}
r-=a-(a&(a>>1));
返回r;
}
uint32_t_uu umulhi(uint32_t a,uint32_t b)
{
返回(uint32_t)((uint64_t)a*b)>>32;
}
#endif//USE_OWN_intrinsic
/*
*对于[1,4]中的每个子区间,使用8位近似值r表示倒数
*平方根。为了加快后续牛顿-拉斐逊迭代,在
*该表包含两条信息:最低有效10位
*存储3*r,最高有效22位存储r**3,从24向下舍入到
*22位,以优化精度。
*/
uint32\u t rsqrt\u选项卡[96]=
{
0xfa0bdefa、0xee6af6ee、0xe5effae5、0xdaf27ad9、,
0xd2eff6d0、0xc890aec4、0xc10366bb、0xb9a71ab2、,
0xb4da2eac、0xadce7ea3、0xa6f2b29a、0xa279a694、,
0x9beb568b、0x97a5c685、0x9163027c、0x8d4fd276、,
0x89501e70、0x8563da6a、0x818ac664、0x7dc4fe5e、,
0x7a122258、0x7671be52、0x72e44a4c、0x6f68fa46、,
0x6db22a43、0x6a52623d、0x67041a37、0x65639634、,
0x622ffe2e、0x609cba2b、0x5d837e25、0x5bfcfe22、,
0x58fd461c、0x57838619、0x560e1216、0x53300a10、,
0x51c72e0d、0x50621a0a、0x4da48204、0x4c4c2e01、,
0x4af789fe、0x49a689fb、0x485a11f8、0x4710f9f5、,
0x45cc2df2、0x448b4def、0x421505e9、0x40df5de6、,
0x3fadc5e3、0x3e7fe1e0、0x3d55c9dd、0x3d55d9dd、,
0x3c2f41da、0x39edd9d4、0x39edc1d4、0x38d281d1、,
0x37bae1ce、0x36a6c1cb、0x3595d5c8、0x3488f1c5、,
0x3488fdc5、0x337fbdc2、0x3279ddbf、0x317749bc、,
0x307831b9、0x307879b9、0x2f7d01b6、0x2e84ddb3、,
0x2d9005b0、0x2d9015b0、0x2c9ec1ad、0x2bb0a1aa、,
0x2bb0f5aa、0x2ac615a7、0x29ded1a4、0x29dec9a4、,
0x28fabda1、0x2819e99e、0x2819ed9e、0x273c3d9b、,
0x273c359b、0x2661dd98、0x258ad195、0x258af195、,
0x24b71192、0x24b6b192、0x23e6058f、0x2318118c、,
0x2318718c、0x224da189、0x224dd989、0x21860d86、,
0x21862586、0x20c19183、0x20c1b183、0x20001580
};
/*此函数用于计算