[Edit1]修复代码
最近发现即使测试确定一切正常,结果也会出现问题,因此我深入挖掘并发现我的方程式中有一个愚蠢的错误,并且由于与我的 pgm 环境的名称冲突,测试得到误报,所以我忽略了它前。现在它在所有情况下都可以正常工作。
我能想到的最好的事情(除了近似或大的LUT)是二分搜索没有乘法,这里是C++ 代码:
//---------------------------------------------------------------------------
WORD u32_sqrt(DWORD xx) // 16 T
{
DWORD x,m,a0,a1,i;
const DWORD lut[16]=
{
// m*m
0x40000000,
0x10000000,
0x04000000,
0x01000000,
0x00400000,
0x00100000,
0x00040000,
0x00010000,
0x00004000,
0x00001000,
0x00000400,
0x00000100,
0x00000040,
0x00000010,
0x00000004,
0x00000001,
};
for (x=0,a0=0,m=0x8000,i=0;m;m>>=1,i++)
{
a1=a0+lut[i]+(x<<(16-i));
if (a1<=xx) { a0=a1; x|=m; }
}
return x;
}
//---------------------------------------------------------------------------
标准二分查找sqrt(xx) 将x 的位从MSB 设置为LSB,从而得到x*x <= xx 的结果。幸运的是,我们可以通过简单地将事物重写为递增被乘数来避免乘法...在每次迭代中,旧的 x*x 结果可以像这样使用:
x1 = x0+m
x1*x1 = (x0+m)*(x0+m) = (x0*x0) + (2*m*x0) + (m*m)
其中x0 是上次迭代中x 的值,x1 是实际值。 m 是实际处理位的权重。 (2*m) 和 (m*m) 是常量,可以用作 LUT 和位移,因此无需相乘。只需要添加。遗憾的是,迭代绑定到顺序计算禁止并行化,因此结果最多为16T。
代码中a0代表最后一个x*x,a1代表实际迭代的x*x
如您所见,sqrt 是在 16 x (BitShiftLeft,BitShiftRight,OR,Plus,Compare) 中完成的,其中位移位和 LUT 可以硬连线。
如果与其他门相比,你有超快的门,你可以将输入时钟乘以 16 并将其用作 SQRT 模块的内部时序。类似于在旧的英特尔 CPU/MCU 中有 MC 时钟作为源 CPU 时钟的划分的旧日子......这样你可以得到1T 计时(或倍数取决于倍率)。