1. 平方根运算软件算法
1.1 二分法
利用二分进行开平方的思想很简单:假定中值为最终解。假定下限为0,上限为x,然后求中值;然后比较中值的平方和x的大小,并根据大小修改下限或者上限;重新计算中值,开始新的循环,直到前后两次中值的距离小于给定的精度为止。**需要注意的一点是,如果x小于1,我们需要将上限置为1。
代码如下:
|
|
这里有一点需要特别注意:在精度判别时不能利用上下限而要利用前后两次mid值,否则可能会陷入死循环!这是因为由于精度问题,在循环过程中可能会产生mid值和up或low中的一个相同。这种情况下,后面的计算都不会再改变mid值,因而在达不到精度内时就陷入死循环。
1.2 牛顿迭代法
将中值替换为切线方程的零根作为最终解,原理可以利用下图解释:
具体代码:
注意到代码中初始值的选择,解释如下:
IEEE浮点标准用的形式来表示一个数,将该数存入float类型之后变为:
现在需要对这个浮点数进行开方,我们看看各部分都会大致发生什么变化。指数E肯定会除以2,127保持不变,m需要进行开方。由于指数部分是浮点数的大头,所以对指数的修改最容易使初始值接近精确值。幸运的是,对指数的开平方我们只需要除以2即可,也即右移一位。但是由于E+127可能是奇数,右移一位会修改指数,我们将先将指数的最低位清零,这就是& 0xff7fffff的目的。然后将该转换后的整数右移一位,也即将指数除以2,同时尾数也除以2(其实只是尾数的小数部分除以2)。由于右移也会将127除以2,所以我们还需要补偿一个64,这就是最后还需要加一个(64<<23)的原因。
这里大家可能会有疑问,最后为什么加(64<<23)而不是(63<<23),还有能不能不将指数最后一位清零?答案是都可以,但是速度都没有我上面写的快。这说明我上面的估计更接近精确值。下面简单分析一下原因。首先假设e为偶数,不妨设e=2n,开方之后e则应该变为n,127保持不变,我们看看上述代码会变为啥。e+127是奇数,会清零,这等价于e+126,右移一位变为n+63,加上补偿的64,指数为n+127,正是所需!再假设e为奇数,不妨设e=2n+1,开方之后e应该变为n+1(不精确),127保持不变,我们看看上述代码会变为啥。e+127是偶数等于2n+128,右移一位变为n+64,加上补偿的64,指数为n+1+127,也是所需!这确实说明上述的估计比其他方法更精确一些,因而速度也更快一些。
1.3 卡马克算法—快速平方根倒数算法
此法实质上是进行了1~2次的牛顿迭代法求开方倒数,求平方根的倒数,实际就是求方程$$ 1/(x^2)-a=0 $$的解。将该方程按牛顿迭代法的公式展开为:$$ x_{(n+1)}=1/2x_n(3-ax_nx_n) $$。这个方法的一个关键地方还在于起始值的选取,算法选取一个“magic number”,使算法的初始解非常接近精确解!
|
|
2. 整数平方根———硬件加速算法
2.1 手工开平方算法法
先以10进制为例,解释手动开平方算法:
首先将数字每两位分成一段。如:745836942。就分成:
7|45|83|69|42,即从个位开始分。共分成五段,第一段是7。对第一段的数字7开方取整,可得a=2。此时,要在2后面接一个数字b,并在7后面加上下一段的数45,使产生的两位数ab的平方不大于745。
我们知道,数ab实际值表示为10a+b,其平方是$$100a^2+20ab+b^2$$。我们可以暂时忽略$$b^2$$,而产生一个“试商”b。即$$b = (745 - 100a^2) / 20a = (745 - 10022) / (20 * 2) = 8.625 ≈ 8$$(向下取整)。但是,我们发现$$28^2 = 784>745$$,于是这个试商需要减少为7。然而,当a=0时,上述求试商的方法不在适用,但我们可以直接取下一段的两位数开方。如√45≈6。求出试商后,用$$745-ab^2$$得到新的“第一段”的数。
具体过程:
取出第一段的数mn,开方得到a,然后接上第二段的数pq,用$$mnpq-100a^2$$得到“余数”x, $$x/20a$$得到试商b,然后调整b(当$$ 20ab+bb>x $$时b需要减少),调整后,将 $$x-20ab-b*b$$作为新的余数x’,ab就是第二轮开平方的结果。由于前面已经将$$100a^2$$减掉,所以后面每次都不用再减去$$100a^2$$。重复步骤,直到开方完毕,或达到要求的精度为止。最后得到的a就是平方根。
如求$$745836942$$的平方根:
$$7|45|83|69|42$$
$$①a=sqrt(7)≈2,b=(745-400)/40≈8,28^2=784>745 ==> b=7 ==> 27^2=729<745,745-729 = 16$$
$$②a=27,b=1683/540≈3,27203+3*3=1629<1683,1683-1629 = 54$$
$$③a=273,b=5469/5460≈1,273201+1*1=5461<5469 ==""> b=1, 5469-5461=8$$5469>
当运用到二进制时,数ab实际值表示为a<<1+b,其平方是$$a^2<<2+ab<<2+b^2$$。我们可以暂时忽略$$b^2$$,而产生一个“试商”b,依此类推下去。以下代码为求一个32位数的平方根,算法将原始值两两分组进行计算,注意:根应至多为16位且每次计算的b值只能是0或1:
|
|
将原始值分类大小由2变成4,那么每次要计算2位二进制数值,可能情况有00,01,10,11,00情况时由于不更新结果,直接移动两位即可,所以可不进行处理,内部循环因此为1~3!
也可把分类大小变成8,16…只要是2的倍数均可,算法流程类似!
具体代码如下:
|
|