实现C开方操作

  • Post author:
  • Post category:其他

最近同事参加面试,被问及如何实现x开y次方,第一个想法就是pow函数。细思考虑,面试官应该意图是如何实现pow函数。

先从最简单的开平方考虑:

被开方数如果是1 4 9 16 等这些数,int 上遍历还有意义 ,但是如果类似5 或者double类型,那又该如何做呢?

1 二分法 

#include <iostream>
#define PRECISION_THRESHOLD 0.0000001
double my_sqrt(double num){
    double result = num/2.0;
    double temp = result * result;
    double pre_low= 0;
    double pre_high = num>1?num:1 ;//避免纯小数  纯小数的开方大于其本身
    while((temp>= (num+PRECISION_THRESHOLD)) || (temp <= (num - PRECISION_THRESHOLD))){
        if(temp<num){
            pre_low = result;
            result = (result+pre_high)/2.0;
        }else if (temp > num){
            pre_high = result;
            result = (result+pre_low)/2.0;
        } else
            break;
        temp = result*result;
    }
    return result;
}
int main(){
    double data = my_sqrt(0.4);
    std::cout<<"data " << data<<std::endl;
    return 0;
}

其中因为是double类型,无法完全相等(5 开方后平方一直在5 附近 比如4.999998 5.000001 然而无法等于5 陷入死循环)所以要设置一个阈值。设置阈值的话又会影响到0.01的开方,在这种情况下结果为 data 0.0999997

2 高斯牛顿法

可得 在Y=X^2时 Y = Xo^2 +2*Xo(X-Xo)  可得 X =( Y+Xo^2)/Xo  并且可知X=Xo时 等式成立 故循环迭代直到 X=Xo

#include <iostream>
#include <cmath>
#define PRECISION_THRESHOLD 0.0000001
double my_sqrt_newton(double num){
    double X = num;
    double pre_X =  num/2.0;
    while(fabs(X-pre_X)> PRECISION_THRESHOLD){
        pre_X = X;
        X = (num + pre_X*pre_X)/(2*pre_X);
    }
    return X;
}
int main(){
    double result = my_sqrt_newton(0.01);
    std::cout<<"result " << result<<std::endl;

    return 0;
}

3 雷神之锤算法

看的也是一脸懵逼

float InvSqrt (float x){
    float xhalf = 0.5f*x;
    int i = *(int*)&x;
    i = 0x5f3759df - (i>>1);
    y = *(float*)&i;
    y = y*(1.5f - xhalf*x*x);
    return x;
}

详情看 http://www.matrix67.com/data/InvSqrt.pdf,神器的数字

部分参考https://blog.csdn.net/xusiwei1236/article/details/25657611


版权声明:本文为liuyanhit原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。