如何在FPGA上对sqrt(x)执行小数值逼近


8

我正在尝试实现一个定点例程,该例程涉及计算的值 XX 接近 0。目标架构是FPGA。一个问题是,该函数无法轻易地将其扩展为使用泰勒展开式。可以看到,对于x的较小值,X 当无穷大 X 方法 0,因此使用幂级数评估函数需要将大系数乘以小 X。因此,该方法在数值上不稳定。

使用迭代方法,Newton-Raphson产生以下迭代方程: Xñ+1个=Xñ2-α2Xñ,我们正在尝试近似 α。但再次,因为α 是小, Xñ同样,解决方案要收敛就必须很小。由于方程式涉及将一个小数除以另一个小数,因此定点算法很可能会失败。

有了这个,我想知道如何为 X 使用定点算术,或者使用预计算系数或迭代方法。


2
如果您以FPGA为目标,那么第一个也是最重要的问题是您想要哪种精度。您说要使用固定点:输入的精度是多少,结果的精度是多少?在定点(如整数)中没有“逼近零”。还有就是,你有兴趣的次数最少。
菲利普

Answers:


5

我以前使用过的例程(不知道它是否是一个“适当的”例程)是一种分而治之的方法。

您从一个任意的上限值和下限值(分别为5和0-您要查找的最高和最低平方根)开始,然后找到它们之间的中点。平方那个值。

如果平方值大于目标值,则将上限值设置为您的平方值。如果较低,则设置较低的值。

重复该操作,直到平方值与您的查找值匹配,或者您执行了足够的迭代以达到所需的精度。

这是我在perl中组合在一起的一个小版本:

#!/usr/bin/perl

my $val = shift;

my $max = 5;
my $min = 0;

my $iterations = 0;
my $maxiter = 40;

while(($max > $min) and ($iterations<$maxiter))
{
    $iterations++;
    my $diff = $min + ($max - $min) / 2;
    my $square = $diff * $diff;

    if($square == $val)
    {

        print "Square root found at $diff\n";
        print "$iterations iterations\n";
        exit(0);
    } else {
        if($square > $val)
        {
            $max = $diff;
        } else {
            $min = $diff;
        }
    }
}

my $diff = $min + ($max - $min) / 2;
print "Approximate square root after $iterations iterations: $diff\n";

当然这是使用浮点数,但是可以很容易地将其添加到定点数。您可以通过更改迭代极限来更改精度。每次迭代都比以前的迭代略精确。

例如:-找到9的平方根:

Approximate square root after 40 iterations: 2.99999999999955
   - or - 
Approximate square root after 10 iterations: 3.00048828125
   - or - 
Approximate square root after 5 iterations: 3.046875

如果找到值3,则当然会早早停止。

给它足够的迭代,它应该非常准确:

./sqrt.pl 0.00284
Square root found at 0.0532916503778969
59 iterations

2
基本上是二进制搜索。
rfusca

您知道选择起始值的方法吗?
Ang Zhi Ping

它是您希望处理的最大数目的平方根。
Majenko


3

您没有指定“小值”或“近似值”的含义。因此,我要提出的建议可能不起作用,但是可以了。

最简单的方法是制作一个查询表。本质上是ROM,其中地址总线是您想要平方根的数字,而数据输出是结果。使用单个BRAM,您可以执行9位输入,8位输出的LUT。当然,更多的BRAM将为您提供更大的桌子。

(BRAM =块RAM的Xilinx术语,也可以用作ROM。其他FPGA的情况与此类似。)

如果您希望获得比BRAM更高的精度,则可以对两个LUT条目进行简单的线性插值。例如,假设您想要一个12位输入,但是只有10位的BRAM。您将输入的前10位放在LUT中查找。在这10位中加1,也查找该值。然后,您可以在两个结果之间进行简单的线性插值,使用低2位告诉您一个值相对于另一个值的比例。当然,这只会给您一个近似值,但是我认为,如果您进行数学计算,您会发现它可能就足够了。

对于低值数字,此方法的准确性最低,但是当输入值较高时,精度会提高。

对上述方法的优化是将BRAM用作双端口ROM。这样,您可以读出两个值,而无需增加使用的BRAM数量。这也使您可以计算每个时钟周期的SQRT,并带有一些流水线延迟。

顺便说一句,此方法也适用于SINE / COSINE!


小值表示x接近0,这就是为什么我对\ sqrt {x}的小值近似值很感兴趣。
Ang Zhi Ping

1
@angzhiping“接近零”没有帮助。我们需要知道范围和准确性。您给出的只是范围的一半,而没有准确性。最终结果是知道输入和输出位数。同样重要的是所需的速度:就时钟速度和每sqrt的时钟而言。

3

尝试以下方法

  • 如果数字为负,请进行相应处理。
  • 如果数字为0,则返回0。
  • 除此以外:
  • 归一化为[1/4,1]范围内的数字:计算将k乘以4(x <<= 2以C为单位)直到达到上述范围的次数。
  • 使用任意方法(多项式逼近,sqrt a [n] =(a [n-1] + k / a [n-1])/ 2的牛顿法等)来计算此范围内的平方根
  • 非规范化:右移k位

0

尝试 X=ÿ+d2ÿ2+2dÿ 所以让 d=X-ÿ2/2ÿ=X/ÿ-ÿ1个 接下来 ÿ=ÿ+d 如果MSb 从右开始是n,则首先ÿ=1个ñ/2。在<4个迭代中收敛。


0

尝试:改进对第一个变量的猜测

可以考虑您的数字:A * 2 ^ n那么
第一个近似是:A * 2 ^(n / 2)

假设您使用的是32位数字,其中24位用于保留分数。对于大于1的数字:
1.计算整数部分(N)中使用的位数
。2.将这个数字减半(N'= N / 2,即,右移1位)
3.将原始数字右移N' :这是您的第一个猜测。

以这种格式,您可以拥有的最小数字是2 ^ -24。平方根约为2 ^ -12。因此,对于小于1的数字:
1.计算分数中的“零”位数,直到达到设置的位数(N)
。2.将这个数字减半(N'= N / 2,即右移1位)
3.将原始数字向左移动经过修改的计数:这是您的第一个猜测。

示例:
0.0000 0000 0000 0000 1 [16个前导零]近似为:0.0000 0000 1

最后,如果您仍然对小A仍有疑问,可以计算1 / A吗?
如果是这样,则将您的数字取反,然后尝试使用平方根逆算法:
x' = 0.5x * (3 - Ax^2)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.