二分法
浮点开方也就是给定一个浮点数x,求根号x。这个简单的问题有很多解,我们从最简单最容易想到的二分开始讲起。利用二分进行开平方的思想很简单,就是假定中值为最终解。假定下限为0,上限为x,然后求中值;然后比较中值的平方和x的大小,并根据大小修改下限或者上限;重新计算中值,开始新的循环,直到前后两次中值的距离小于给定的精度为止。需要注意的一点是,如果x小于1,我们需要将上限置为1,原因你懂的。
代码如下:
#define eps 2^(-51)
float SqrtByBisection(float n){
float low, up, mid, last;
low = 0, up = (n < 1 ? 1 : n);
mid = (low + up) / 2;
do{
if (mid * mid > n)up = mid;
else low = mid;
last = mid;
mid = (low + up) / 2;
} while (fabsf(mid - last) > eps);
return mid;
}
这种方法非常直观,也是面试过程中经常会问到的问题,不过这里有一点需要特别注意:在精度判别时不能利用上下限而要利用前后两次mid值,否则可能会陷入死循环!这是因为由于精度问题,在循环过程中可能会产生mid值和up或low中的一个相同。这种情况下,后面的计算都不会再改变mid值,因而在达不到精度内时就陷入死循环。但是改为判断前后两次mid值就不会有任何问题(为啥自己想)。大家可以找一些例子试一下,这可以算是二分法中的一个trick。二分虽然简单,但是却有一个非常大的问题:收敛太慢!也即需要循环很多次才能达到精度要求。这也比较容易理解,因为往往需要迭代3到4次才能获得一位准确结果。为了能提升收敛速度,我们需要采用其它的方法。
牛顿迭代法
原理也比较简单,就是将中值替换为切线方程的零根作为最终解。原理可以利用下图解释:
#define eps 2^(-51)
float SqrtByNewton(float x){
float val = x, last;
do{
last = val;
val = (val + x / val) / 2;
} while (fabsf(val - last) > eps);
}