搜索
bottom↓
回复: 95

【转载】世界上最快的浮点开方算法

  [复制链接]

出0入0汤圆

发表于 2015-3-1 18:40:57 | 显示全部楼层 |阅读模式
【转载】http://blog.csdn.net/wangsanhuai2010/article/details/5570730

任何一个3D引擎都是通过其内部的数学模型和实现工具来展现它的力量与速度的,Quake III中使用了一个非常有意思的技巧来计算平方根倒数(inverse square root)

Carmack's 不寻常平方根倒数
第一个跳出来的便是对函数Q_rsqrt中对0x5f3759df的使用,这个数计算了一个浮点数的inverse square root,但是为什么这个函数有这样的功能呢?
观察q_math.c原本的函数:

  1. float Q_rsqrt( float number )   
  2. {  
  3.   long i;   
  4.   float x2, y;   
  5.   const float threehalfs = 1.5F;   
  6.   x2 = number * 0.5F;   
  7.   y  = number;  
  8.   i  = * ( long * ) &y;  // evil floating point bit level hacking   
  9.   i  = 0x5f3759df - ( i >> 1 );  
  10.   y  = * ( float * ) &i;  
  11.   y  = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration   
  12.   y  = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed   
  13.   return y;   
  14. }
复制代码
   

它不仅有效,甚至在某些CPU上,Carmack的Q_rsqrt 比(float)(1.0/sqrt(x)的计算快4倍,尽管sqrt()通常使用的是FSQRT的汇编指令!
在另一个文件code/common/cm_trace.c 中,我们发现了更简洁的对同样HACK的实现。这一次,它被用来计算一个float - sqrt(x)的平方根。注意,其中的唯一不同是在返回值上--用返回*y取代了返回y。  
  
  1. float SquareRootFloat(float number) {  
  2.     long i;   
  3.     float x, y;   
  4.     const float f = 1.5F;   
  5.     x = number * 0.5F;  
  6.     y  = number;   
  7.     i  = * ( long * ) &y;   
  8.     i  = 0x5f3759df - ( i >> 1 );   
  9.     y  = * ( float * ) &i;   
  10.     y  = y * ( f - ( x * y * y ) );   
  11.     y  = y * ( f - ( x * y * y ) );   
  12.     return number * y;   
  13. }
复制代码


牛顿对根的近似值
上面的代码执行了众所周知的牛顿对根的近似值,像绝大多数其它迭代求近似值的计算一样,牛顿近似值假定是迭代的;每一次迭代都增强了它的准确度直至达到需要的准确度。
在牛顿近似值中的一般想法是我们我们猜测一个数x的平方根值y,我们可能通过一个简单的操作用x/y来拉平y来取得更好的猜测,使其更接近实际的平方根,例如,我们像下面这样计算2的平方根,我们假定初始的猜测是1:
  
  1. 2/1 = 2 ;  (2 + 1) / 2 = 1.5   
  2. 2/1.5 = 1.3333; ( 1.5 + 1.3333 ) / 2 = 1.4167   
  3. 2/1.4167 = 1.4117;  ( 1.4167 + 1.4117 ) / 2 = 1.4142   
  4. And so on...   
复制代码


如前面所提到的,牛顿的近似值是一个大家所熟知的用以快速计算平方根的方法。但是,Carmack在初始的猜测中就选取的不寻常的值,它彻底加强了准确度并且将Quake III中计算所要的值的迭代次数降到了1次!

魔数
  这个函数中真正有意思的方面是神奇的常量0x5f3759df,用来计算初始猜测的,在
  
  1. i  = 0x5f3759df - ( i >> 1 );  
复制代码
 

  因此,把输入除以2并从神奇常量中减去。这个常数工作起来几乎是完美的--对于一个 low relative error of 10^-3来说只要一次牛顿近似值迭代就够了。如评论中第二次迭代中展示的,这个近似值对Quake III引擎来说已经足够了。

  结果,这个神奇的常数0x5f3759df成了一个迷了,在文章"Fast Inverse Square Root",普度大学的数学家Chris Lomont研究了这个常数,用了几种精细的技术,Lomont想自己用数学方法求出这个常数来,结果令人惊奇--Lomont用数学方法计算出来的最佳常数(0x5f37642f)有一点点不同,并且除了理论上强一些之外,它产生的结果并没有源代码中使用的原始常数好!确实,John Carmack 一定用了天才般的黑盒来找到这个常数。
  只是仅仅从数字上来找的方法中,Lomont找到了一个更好的常数,这个数比原始的那个强了那么一点点。然而,实践中两个常数产生了大概相同的结果,Lomont提出这个使用了更好的常数的函数:


  1. float InvSqrt(float x)   
  2. {   
  3.   float xhalf = 0.5f*x;   
  4.   int i = *(int*)&x;   
  5.   i = 0x5f375a86- (i>>1);   
  6.   x = *(float*)&i;   
  7.   x = x*(1.5f-xhalf*x*x);   
  8.   return x;   
  9. }
复制代码
   

-----------------------------------------------------------------------------------------------------------------------------------------------

-----------------------------------------------------------------------------------------------------------------------------------------------

-----------------------------------------------------------------------------------------------------------------------------------------------

  关于本文,东拼西凑来的,今天也已经很晚了。只是最近我在研究图形学算法的过程中,一些数字的数学的方法常常让我感到震惊,我惊叹于数学竟是如此的神奇。黑盒的应用是一门大学问,那些伟大的但是并不一定很出名的数学家常常利用黑盒另外开辟了区别于经典算法的道路,但却能带来非常人能想像的效率。在研究这些强人的作品的时候,能得到一种很美妙很利落的体会,好的算法就就像好的音乐,当欣赏他们的时候,只想拿起吉它,轻轻的弹上一首……

  以其说计算机创造了21世纪,还不如说算法与数据结构创造了21世纪。前人的智慧也常让我感到无比的惊叹,而已往我却没有过这种深刻的体会,应视为较近距离接触了吧。最近在研究卡尔曼滤波器、小波变换理论、数据融合算法、以及复杂数据结构的过程中,真正体会到了什么才叫旷世绝学,虽然现在自己也只是略懂丁点儿,但这些来自前人的伟大思想,却让我振奋,让我更加明智。

出10入0汤圆

发表于 2015-3-1 18:55:13 | 显示全部楼层
最快的浮点开方算法,先看看

出0入0汤圆

发表于 2015-3-1 18:55:55 | 显示全部楼层
数学就是那么的神奇。

出0入0汤圆

发表于 2015-3-1 19:35:19 | 显示全部楼层
学习了,神奇的0x5f3759df。

出0入0汤圆

发表于 2015-3-1 20:09:25 | 显示全部楼层
很有意思。。

出0入0汤圆

发表于 2015-3-1 20:14:48 | 显示全部楼层
神奇的常数

出0入0汤圆

发表于 2015-3-1 20:30:46 | 显示全部楼层
这个神奇的数以前在本坛出现过,忘记帖子叫啥名了。。

出0入0汤圆

发表于 2015-3-1 21:03:15 | 显示全部楼层
作者也说了,这是求近似值,精度实在是有限
我试过用来做姿态解算里的归一化,相对F103没有FPU来说确实是快了一些,但是对于有DSP的F4来说不仅慢而且不精确。

出0入0汤圆

发表于 2015-3-1 21:16:20 | 显示全部楼层
maxwelllls 发表于 2015-3-1 21:03
作者也说了,这是求近似值,精度实在是有限
我试过用来做姿态解算里的归一化,相对F103没有FPU来说确实是快 ...

先记下来,等有空用我手头的f407试一下你说的对不对。

出0入0汤圆

发表于 2015-3-1 21:33:29 | 显示全部楼层
这个以前在8051上用过,确实快了很多,神奇的数学

出0入0汤圆

发表于 2015-3-1 21:35:36 | 显示全部楼层
按10进制算,这个神奇常数比最佳常数小了2640,即:
    1597463007 - 1597465647 = -2640
对应相对值约为:
    2640/1597463007≈1.653e-6
神奇常数小了约百万分之1.653 。

出0入0汤圆

发表于 2015-3-1 21:39:12 | 显示全部楼层
先收藏了,谢谢分享!

出0入0汤圆

发表于 2015-3-2 08:41:47 | 显示全部楼层
请教楼主,预计效率提高多少倍?

出0入0汤圆

发表于 2015-3-2 08:45:18 | 显示全部楼层
................

出0入0汤圆

发表于 2015-3-2 08:46:25 | 显示全部楼层
收藏了,谢谢!

出0入0汤圆

发表于 2015-3-2 08:54:48 | 显示全部楼层
这么神奇?赶紧记录下来。

出0入0汤圆

发表于 2015-3-2 09:04:11 | 显示全部楼层
感谢分享,看看

出0入0汤圆

发表于 2015-3-2 09:08:44 | 显示全部楼层
mark, 0x5f3759df 浮点开方运算

出0入4汤圆

发表于 2015-3-2 09:14:12 | 显示全部楼层
记下试试看

出0入0汤圆

发表于 2015-3-2 09:37:16 | 显示全部楼层
在一个项目上用过,速度快了很多

出0入0汤圆

发表于 2015-3-2 09:38:58 | 显示全部楼层
为何冠名最快,有人实测过吗?不过还是可以记下来留待以后应用。

出0入0汤圆

发表于 2015-3-2 09:51:49 | 显示全部楼层
神奇的数学,看不懂

出0入8汤圆

发表于 2015-3-2 10:08:58 | 显示全部楼层
数学是神奇的,这个算法收藏了,呵呵

出0入0汤圆

发表于 2015-3-2 10:11:02 | 显示全部楼层
这么神奇,做个记号。

出0入0汤圆

发表于 2015-3-2 10:11:05 | 显示全部楼层
数值分析相关的内容。

出0入0汤圆

发表于 2015-3-2 10:30:14 | 显示全部楼层
没有看懂,用的时候再细看吧

出0入0汤圆

发表于 2015-3-2 10:34:04 | 显示全部楼层
学习。。。做好记号

出0入0汤圆

发表于 2015-3-2 10:55:26 | 显示全部楼层
看不懂。。。看来得好好学习了

出0入0汤圆

发表于 2015-3-2 11:02:24 来自手机 | 显示全部楼层
以前遊戲雷神之錘的解法

出0入0汤圆

发表于 2015-3-2 11:13:10 | 显示全部楼层
bangbangji 发表于 2015-3-1 20:30
这个神奇的数以前在本坛出现过,忘记帖子叫啥名了。。

doom启示录里面的Quake-III Arena里面使用的3D引擎使用了这个数,
我也是在本论坛上第一次知道0x5f3759df这个数的;

出0入0汤圆

发表于 2015-3-2 11:40:38 | 显示全部楼层
神奇的0x5f3759df ……

出0入0汤圆

发表于 2015-3-2 11:42:34 | 显示全部楼层
搜了一下发现一句注释:

i = 0x5f3759df - ( i >> 1 ); // what the fuck?

出0入0汤圆

发表于 2015-3-2 11:52:39 | 显示全部楼层
在AVR单片机MEGA16上试过,这个方法比最普通的SQRT函数来说,一般可以记节约75%的时间。

出0入0汤圆

发表于 2015-3-2 12:32:38 | 显示全部楼层
学习了,受教了,,,

出0入14汤圆

发表于 2015-3-2 12:38:21 | 显示全部楼层
很早论坛就有这个算法的帖子了,虽然我表示看不懂

出0入0汤圆

发表于 2015-3-2 13:31:55 来自手机 | 显示全部楼层
谢谢分享

出0入0汤圆

发表于 2015-3-2 15:11:15 | 显示全部楼层
用过,确实很快

出0入4汤圆

发表于 2015-3-2 15:13:07 | 显示全部楼层
顶起~~~~

出0入0汤圆

发表于 2015-3-3 08:53:51 | 显示全部楼层
抽空  好好研究下  算法 累啊!!

出0入0汤圆

发表于 2015-3-3 09:29:45 | 显示全部楼层
好东西,收藏

出0入0汤圆

发表于 2015-3-3 10:05:30 | 显示全部楼层
收藏一下!!!

出0入0汤圆

发表于 2015-3-3 11:11:29 | 显示全部楼层
好东西,看看试下

出0入0汤圆

发表于 2015-3-3 12:39:06 | 显示全部楼层
我数学不是很懂啊,唉

出0入12汤圆

发表于 2015-3-3 12:45:48 | 显示全部楼层
有字节序问题吗?

出0入0汤圆

发表于 2015-3-3 12:57:34 | 显示全部楼层
还是牛人多啊,我先学习了!

出0入0汤圆

发表于 2015-3-3 13:12:01 | 显示全部楼层
还是挺有意思的,有机会了在自己的程序中用一下

出0入0汤圆

发表于 2015-3-3 13:14:27 | 显示全部楼层
感谢分享!

出0入0汤圆

发表于 2015-3-3 13:17:20 | 显示全部楼层
很流弊的样子,顶起来

出0入0汤圆

发表于 2015-3-3 13:25:32 | 显示全部楼层
最快的浮点开方算法,先看看

出0入0汤圆

发表于 2015-3-3 13:56:23 | 显示全部楼层
世界上最快的浮点开方算法

出0入0汤圆

发表于 2015-3-3 15:34:28 来自手机 | 显示全部楼层
收藏一下

出0入0汤圆

发表于 2015-3-3 15:47:28 | 显示全部楼层
最快的浮点开方算法

出0入0汤圆

发表于 2015-3-3 17:00:29 | 显示全部楼层
很早就知道这个东西了, 雷神之锤源码开源的时候就广为流传, 用这个方法要看你的精度要求

出0入0汤圆

发表于 2015-3-4 13:58:45 | 显示全部楼层
嗯,一直使用的,精度还可以接受

出0入0汤圆

发表于 2015-3-4 16:25:52 | 显示全部楼层
谢谢,慢慢学习下

出0入0汤圆

发表于 2015-3-4 17:13:34 | 显示全部楼层
最精简的1/sqrt()函数:
float InvSqrt(float x)
{
    float xhalf = 0.5f*x;
    int i = *(int*)&x; // get bits for floating VALUE
    i = 0x5f375a86- (i>>1); // gives initial guess y0
    x = *(float*)&i; // convert bits BACK to float
    x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy
    return x;
}  

===============
mark

出0入0汤圆

发表于 2015-3-4 18:24:25 | 显示全部楼层
好东西,验证一下。

出0入0汤圆

发表于 2015-3-4 18:44:19 | 显示全部楼层
厉害!有空试试。

出0入0汤圆

发表于 2015-3-4 20:03:35 | 显示全部楼层
maxwelllls 发表于 2015-3-1 21:03
作者也说了,这是求近似值,精度实在是有限
我试过用来做姿态解算里的归一化,相对F103没有FPU来说确实是快 ...

f4的dsp库也是用这个数字来开方的

出0入0汤圆

发表于 2015-3-4 20:23:00 | 显示全部楼层
不明觉厉

出0入0汤圆

发表于 2015-3-4 20:36:47 | 显示全部楼层
xwy19920211 发表于 2015-3-4 20:03
f4的dsp库也是用这个数字来开方的

不是用这个数,用sqrt算的是很精确的数,跟卡西欧的计算器算出来的一样

出0入0汤圆

发表于 2015-3-4 21:05:32 | 显示全部楼层
数学永远都是这么神奇。。。。   谢谢分享

出5入42汤圆

发表于 2015-3-5 10:00:08 | 显示全部楼层
数学成绩差的表示看不懂啊

出0入0汤圆

发表于 2015-3-5 10:22:59 来自手机 | 显示全部楼层
有木有0x5f3759df的数学原理啊,表示这样看只知道有用但是终究不是自己的东西

出0入0汤圆

发表于 2015-3-5 14:26:58 | 显示全部楼层
mack,不知道0x5f3759df这个数是怎么发现的。

出10入95汤圆

发表于 2015-3-7 15:45:15 | 显示全部楼层
不错!谢谢分享!!

出0入0汤圆

发表于 2015-3-7 16:04:37 | 显示全部楼层
卡马克大神的故事,恩

出0入0汤圆

发表于 2015-3-7 16:20:18 | 显示全部楼层
这里有关于这个常数的说明
http://zh.wikipedia.org/zh/平方根倒数速算法

出0入0汤圆

发表于 2015-3-7 18:52:37 | 显示全部楼层
平方根倒数速算法,好东西!

出0入0汤圆

发表于 2015-3-8 00:50:44 | 显示全部楼层
数学,在程序算法中涉及很多。

出0入0汤圆

发表于 2015-3-8 02:08:39 | 显示全部楼层
数学学得好

出0入0汤圆

发表于 2015-3-8 09:24:49 | 显示全部楼层

最精简的1/sqrt()函数:
float InvSqrt(float x)
{
    float xhalf = 0.5f*x;
    int i = *(int*)&x; // get bits for floating VALUE
    i = 0x5f375a86- (i>>1); // gives initial guess y0
    x = *(float*)&i; // convert bits BACK to float
    x = x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracy
    return x;
}  

这个不错,以用在交流电压采集真有效值计算程序中

出0入0汤圆

发表于 2015-3-8 15:25:48 来自手机 | 显示全部楼层
maxwelllls 发表于 2015-3-4 20:36
不是用这个数,用sqrt算的是很精确的数,跟卡西欧的计算器算出来的一样

很有可能会用到,数学库也是用迭代算法算的,这个方法可以快速得到一个近似值,收敛更快

出0入0汤圆

发表于 2015-3-8 22:29:38 | 显示全部楼层
感謝分享!

出0入0汤圆

发表于 2015-3-9 09:32:44 | 显示全部楼层
真有在MCU 上用的,这个速度快是肯定的,不过 个精度不能通用,原来的用途是3d和图形处理,要求低的多

出0入0汤圆

发表于 2015-3-9 11:33:11 | 显示全部楼层
第一次听说这个,好神奇的说

出0入0汤圆

发表于 2015-3-9 14:01:32 | 显示全部楼层
用在单精度的,好像用于双精度的也有类似的一个数值

出0入0汤圆

发表于 2015-3-9 19:24:44 | 显示全部楼层
好神奇的数字!

出0入0汤圆

发表于 2015-3-9 22:45:56 | 显示全部楼层
不懂原理,但觉得很神奇,又学到了东西了,有时间也试一下,看看效果,多谢!

出0入0汤圆

发表于 2015-3-10 09:05:53 | 显示全部楼层
参考一下 谢谢分享

出0入0汤圆

发表于 2015-3-10 09:14:09 | 显示全部楼层
很神奇,想不到的快。

出0入0汤圆

发表于 2015-3-10 10:03:54 | 显示全部楼层

        for(i=1;i<200000;i++)
        {
                for(j=1;j<10000;j++)
                f=Q_rsqrt( j ) ;
                if(f>1000000)printf("x=%d=>%f\n",j,f);
        }
        printf ("AAA \n");
        for(i=1;i<200000;i++)
        {
                for(j=1;j<10000;j++)
                f=1.0/sqrt((float)j);
                if(f>1000000)printf("x=%d=>%f\n",j,f);
        }
        printf ("BBB \n");
        getchar();
以上程序,在PC上试过,我的是I3(4130),时间差不多,第一个14秒,第二个16秒左右的样子。

出0入0汤圆

发表于 2015-3-10 12:11:46 | 显示全部楼层
有的算法境界高的另众生无法膜拜

出0入0汤圆

发表于 2015-3-10 12:35:32 | 显示全部楼层
不懂,未用过这个神数。

出0入0汤圆

发表于 2015-3-10 13:09:47 | 显示全部楼层
mark,最快的浮点开方算法,这种好东西收起来备用

出0入0汤圆

发表于 2015-3-10 13:25:17 | 显示全部楼层
感谢分享……

出0入0汤圆

发表于 2015-3-10 16:39:59 | 显示全部楼层
以前只知道那个牛顿迭代方法开跟号,基本上看RP的那种,不知道这个优化过的是不是也要看RP。。

出0入0汤圆

发表于 2015-3-12 08:53:01 | 显示全部楼层
mark,好东西收起来,日后有用

出0入0汤圆

发表于 2015-3-12 09:15:53 | 显示全部楼层
居然还可以这样开方的,卡大英明

出0入0汤圆

发表于 2015-3-12 09:18:31 | 显示全部楼层
以后也许能用到,谢谢

出0入0汤圆

发表于 2015-3-13 15:14:37 | 显示全部楼层
学习了,标记下也许以后会用到。

出0入0汤圆

发表于 2015-3-13 16:40:49 | 显示全部楼层
神奇的数字,数硬合体更神奇!

出0入0汤圆

发表于 2015-3-13 19:19:04 | 显示全部楼层
太神奇了,谢谢啊!

出0入0汤圆

发表于 2022-3-22 00:34:27 | 显示全部楼层

太神奇了,谢谢啊!

出0入0汤圆

发表于 2022-3-24 09:53:26 | 显示全部楼层
Thank you !!!
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-4 20:30

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

快速回复 返回顶部 返回列表