美文网首页
快速平方根导数算法

快速平方根导数算法

作者: GGBond_8488 | 来源:发表于2023-10-26 16:14 被阅读0次

    简介

    如果要求x的平方根倒数 , 一般我们都会写成这个形式1/sqrt(x),里面包含一个除法操作和一个sqrt操作

    针对于基本的加减乘除计算来说,而除法相对于计算机来说是最为复杂的运算

    在某些场景下,大量的除法运算显然是不能满足需求的

    快速平方根算法

    float Q_rsqrt(float number)
    
    {
    
     int32_t i; 
    
     float x2, y; 
    
     const float threehalfs = 1.5f;
    
     x2 = number * 0.5f;
    
     y = number;
    
     i = * (int32_t *) &y;
    
     i = 0x5f3759df - (i >> 1);
    
     y = * (float *) &i;
    
     y = y * (threehalfs - (x2 * y * y));
    
     //y = y * (threehalfs - (x2 * y * y));
    
     //y = y * (threehalfs - (x2 * y * y));
    
     return y;
    
    }//来源于雷神之锤开源代码
    
    

    浮点数在计算机内表示的方式(IEEE754)

    S EEE EEEE E MMM MMMM MMMM MMMM MMMM MMMM

    image.png

    sign exp tails

    二进制只有0和1,以科学记数法的形式表示二进制浮点数的时候,为了提升精度,首位的1会被省略掉

    浮点数的二进制表示:

    image.png

    而浮点数实际的数值表示为如下公式

    image.png image.png image.png image.png image.png

    某种程度上来说,浮点数的二进制表示即为自身的对数 float_x ~= log(float_x)

    c/c++ 数据类型,二进制移位

    移位操作相比除法是很快的

    110=6 11 = 110>>1 = 3 1100=110<<1=12

    111=7 11 = 111>>1 = 3. 1110 = 111<<1 = 14

    内存中存储的二进制数据是固定的,类型描述的是描述数据的元数据

    如果普通的类型转换,会损失小数,也会打乱原来的二进制形式
    i = (int32_t) y y(3.33) -> i(3)

    如果我们想保留原二进制形式,就需要对描述该二进制的类型进行转换,而不是对应的值进行转换(有点像union)

    i = * (int32_t *) &y; y(3.33) -> i(1079320248) 
    

    某种程度上来说,浮点数的二进制表示即为自身的对数 float_x ~= log(float_x)

    i = log(y)

    如果要求1/sqrt(y) 就可以转换为 求 log(1/sqrt(y)) 就可以转换为 - 1/2 log(y) 就可以转换为求 -(i>>1)

    0x5f3759df 这个近似项的计算:

    假设w = 1/sqrt(y) =>

    log(w) = -1/2 log(y) => 设F = \frac{1}{2^{23} }\left ( M + 2^{23} *E \right ) + \mu - 127

    log(w_F) = -1/2 log(y_F) =>

    w_F + u - 127 = -1/2(y_F + u - 127) =>

    w_F = \frac{3}{2} 2^{23} (127 -u) -\frac{1}{2} * y\_F =>

    w_F = 0x5f3759df - (i>>1)

    牛顿迭代法

    求f(x) = 0的解 x_n = x - dx

    dx = (dy / dx) / (dy) = f'(x) / f(x)

    x_n = x - f(x) / f'(x)

    [图片上传失败...(image-65c956-1698394186269)]

    这里的目标公式应该为 f(y)=1/y^2 - x

    y_n =y - f(y) / f'(y)

    = y - \frac{\frac{1}{y^{2} } - x}{- \frac{2}{y^{3} } }

    = y \left (\frac{3}{2} - \frac{1}{2} x y^{2} \right )

    y = y * (threehalfs - (x2 * y * y));
    

    测试代码

    #include<stdio.h>
    
    #include<iostream>
    
    #include<cmath>
    
    #include<ctime>
    
    using namespace std;
    
    float Q_rsqrt(float number);
    
    int main(){
    
     float a = 1.4444444444444444444444444444f;
    
     float ret, ret2;
    
     clock_t start,end;
    
     start = clock(); 
    
     for(int i=0; i <=100000000; i++) {
    
     ret = Q_rsqrt(a);
    
     }
    
     end = clock();
    
     cout<<"time_1: "<<(end - start)<<endl;
    
     start = clock(); 
    
     for(int i=0; i <=1000000000; i++)  {
    
     ret2 = 1/sqrt(a);
    
     }
    
     end = clock();
    
     cout<<"time_2: "<<(end - start)<<endl;
    
     std::cout<<"ret :" <<ret<<std::endl;
    
     std::cout<<"c++ 1/sqrt:" << ret2<<std::endl;
    
     return 0;
    
    }
    
    float Q_rsqrt(float number)
    
    {
    
     int32_t i; //00000000 00000000 00000000
    
     //IEEE 754   //1       8(-127~128)   (23)
    
     float x2, y; //0(sign) 00000000(exp) 000000000000000(tails)
    
     const float threehalfs = 1.5f;
    
     x2 = number * 0.5f;
    
     y = number;
    
     i = * (int32_t *) &y;
    
     i = 0x5f3759df - (i >> 1);
    
     y = * (float *) &i;
    
     y = y * (threehalfs - (x2 * y * y));
    
     y = y * (threehalfs - (x2 * y * y));
    
     y = y * (threehalfs - (x2 * y * y));
    
     return y;
    
    }
    

    测试结果

    编译器 gcc8.2

    0次迭代

    time_1: 599641
    
    time_2: 5312553
    
    ret :0.855104
    
    c++ 1/sqrt:0.83205
    
    

    1次迭代

    time_1: 916537
    
    time_2: 5310806
    
    ret :0.831083
    
    c++ 1/sqrt:0.83205
    
    

    2次迭代

    
    time_1: 1111124
    
    time_2: 5208424
    
    ret :0.832049
    
    c++ 1/sqrt:0.83205
    
    

    3次迭代

    time_1: 1463755
    
    time_2: 5279041
    
    ret :0.83205
    
    c++ 1/sqrt:0.83205
    

    其他

    eigen的rsqrt用的就是标准库的1/ std::sqrt(a)

    相关文章

      网友评论

          本文标题:快速平方根导数算法

          本文链接:https://www.haomeiwen.com/subject/peayidtx.html