企业网站的常见类型有学网站建设工作室
企业网站的常见类型有,学网站建设工作室,WordPress 简历库,数据交换平台我们平时经常会有一些数据运算的操作#xff0c;需要调用sqrt#xff0c;exp#xff0c;abs等函数#xff0c;那么时候你有没有想过#xff1a;这个些函数系统是如何实现的#xff1f;就拿最常用的sqrt函数来说吧#xff0c;系统怎么来实现这个经常调用的函数呢#xf… 我们平时经常会有一些数据运算的操作需要调用sqrtexpabs等函数那么时候你有没有想过这个些函数系统是如何实现的就拿最常用的sqrt函数来说吧系统怎么来实现这个经常调用的函数呢 虽然有可能你平时没有想过这个问题不过正所谓是“临阵磨枪不快也光”你“眉头一皱计上心来”这个不是太简单了嘛用二分的方法在一个区间中每次拿中间数的平方来试验如果大了就再试左区间的中间数如果小了就再拿右区间的中间数来试。比如求sqrt(16)的结果你先试016/288*86464比16大然后就向左移试08/244*416刚好你得到了正确的结果sqrt(16)4。然后你三下五除二就把程序写出来了
//用二分法
float SqrtByBisection(float n)
{ //小于0的按照你需要的处理 if(n 0) return n; float mid,last; float low,up; low0,upn; mid(lowup)/2; do{if(mid*midn)upmid; else lowmid;lastmid;mid(uplow)/2; }//精度控制while(abs(mid-last) eps);return mid;
} 然后看看和系统函数性能和精度的差别其中时间单位不是秒也不是毫秒而是CPU Tick不管单位是什么统一了就有可比性。二分法和系统的方法结果上完全相同但是性能上整整差了几百倍。为什么会有这么大的区别呢难道系统有什么更好的办法难道。。。。哦对了回忆下我们曾经的高数课曾经老师教过我们“牛顿迭代法快速寻找平方根”或者这种方法可以帮助我们具体步骤如下。 求出根号a的近似值首先随便猜一个近似值x然后不断令x等于x和a/x的平均数迭代个六七次后x的值就已经相当精确了。例如我想求根号2等于多少。假如我猜测的结果为4虽然错的离谱但你可以看到使用牛顿迭代法后这个值很快就趋近于根号2了
( 4 2/4 ) / 2 2.25
( 2.25 2/2.25 ) / 2 1.56944..
( 1.56944.. 2/1.56944..) / 2 1.42189..
( 1.42189.. 2/1.42189..) / 2 1.41423..
....这种算法的原理很简单我们仅仅是不断用(x,f(x))的切线来逼近方程x^2-a0的根。根号a实际上就是x^2-a0的一个正实根这个函数的导数是2x。也就是说函数上任一点(x,f(x))处的切线斜率是2x。那么x-f(x)/(2x)就是一个比x更接近的近似值。代入 f(x)x^2-a得到x-(x^2-a)/(2x)也就是(xa/x)/2。 相关的代码如下
float SqrtByNewton(float x)
{// 最终float val x;// 保存上一个计算的值float last;do{last val;val (val x/val) / 2;}while(abs(val-last) eps);return val;
}牛顿迭代法性能提高了很多可是和系统函数相比还是有这么大差距这是为什么呀想啊想啊想了很久仍然百思不得其解。突然有一天我在网上看到一个神奇的方法于是就有了今天的这篇文章废话不多说看代码先
float InvSqrt(float x)
{float xhalf 0.5f*x;int i *(int*)x; // get bits for floating VALUE i 0x5f375a86- (i1); // gives initial guess y0x *(float*)i; // convert bits BACK to floatx x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracyx x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracyx x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracyreturn 1/x;
}这次真的是质变了结果竟然比系统的还要好。到现在你是不是还不明白那个“鬼函数”到底为什么速度那么快吗不急先看看下面的故事吧 Quake-III Arena (雷神之锤3)是90年代的经典游戏之一。该系列的游戏不但画面和内容不错而且即使计算机配置低也能极其流畅地运行。这要归功于它3D引擎的开发者约翰-卡马克John Carmack。事实上早在90年代初DOS时代只要能在PC上搞个小动画都能让人惊叹一番的时候John Carmack就推出了石破天惊的Castle Wolfstein, 然后再接再励doom, doomII, Quake...每次都把3-D技术推到极致。他的3D引擎代码资极度高效几乎是在压榨PC机的每条运算指令。当初MS的Direct3D也得听取他的意见修改了不少API。 最近QUAKE的开发商ID SOFTWARE 遵守GPL协议公开了QUAKE-III的原代码让世人有幸目睹Carmack传奇的3D引擎的原码。这是QUAKE-III原代码的下载地址 http://www.fileshack.com/file.x?fid7547。我们知道越底层的函数调用越频繁。3D引擎归根到底还是数学运算。那么找到最底层的数学运算函数在game/code/q_math.c 必然是精心编写的。里面有很多有趣的函数很多都令人惊奇估计我们几年时间都学不完。在game/code/q_math.c里发现了这样一段代码。它的作用是将一个数开平方并取倒经测试这段代码比(float)(1.0/sqrt(x))快4倍
float Q_rsqrt( float number )
{long i;float x2, y;const float threehalfs 1.5F;x2 number * 0.5F;y number;i * ( long * ) y; // evil floating point bit level hackingi 0x5f3759df - ( i 1 ); // what the fuck?y * ( float * ) i;y y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration// y y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed#ifndef Q3_VM#ifdef __linux__assert( !isnan(y) ); // bk010122 - FPE?#endif#endifreturn y;
} 函数返回1/sqrt(x)这个函数在图像处理中比sqrt(x)更有用。注意到这个函数只用了一次叠代其实就是根本没用叠代直接运算。编译实验这个函数不仅工作的很好而且比标准的sqrt()函数快4倍要知道编译器自带的函数可是经过严格仔细的汇编优化的啊 这个简洁的函数最核心也是最让人费解的就是标注了“what the fuck?”的一句i 0x5f3759df - ( i 1 ); 再加上y y * ( threehalfs - ( x2 * y * y ) ); 两句话就完成了开方运算而且注意到核心那句是定点移位运算速度极快特别在很多没有乘法指令的RISC结构CPU上这样做是极其高效的。 算法的原理其实不复杂,就是牛顿迭代法,用x-f(x)/f(x)来不断的逼近f(x)a的根。 没错一般的求平方根都是这么循环迭代算的但是卡马克(quake3作者)真正牛B的地方是他选择了一个神秘的常数0x5f3759df 来计算那个猜测值就是我们加注释的那一行那一行算出的值非常接近1/sqrt(n)这样我们只需要2次牛顿迭代就可以达到我们所需要的精度。好吧如果这个还不算NB接着看 普渡大学的数学家Chris Lomont看了以后觉得有趣决定要研究一下卡马克弄出来的这个猜测值有什么奥秘。Lomont也是个牛人在精心研究之后从理论上也推导出一个最佳猜测值和卡马克的数字非常接近, 0x5f37642f。卡马克真牛他是外星人吗 传奇并没有在这里结束。Lomont计算出结果以后非常满意于是拿自己计算出的起始值和卡马克的神秘数字做比赛看看谁的数字能够更快更精确的求得平方根。结果是卡马克赢了... 谁也不知道卡马克是怎么找到这个数字的。 最后Lomont怒了采用暴力方法一个数字一个数字试过来终于找到一个比卡马克数字要好上那么一丁点的数字虽然实际上这两个数字所产生的结果非常近似这个暴力得出的数字是0x5f375a86。 Lomont为此写下一篇论文Fast Inverse Square Root。 论文下载地址http://www.math.purdue.edu/~clomont/Math/Papers/2003/InvSqrt.pdf http://www.matrix67.com/data/InvSqrt.pdf。 最后给出最精简的1/sqrt()函数
float InvSqrt(float x)
{float xhalf 0.5f*x;int i *(int*)x; // get bits for floating VALUE i 0x5f375a86- (i1); // gives initial guess y0x *(float*)i; // convert bits BACK to floatx x*(1.5f-xhalf*x*x); // Newton step, repeating increases accuracyreturn x;
} 大家可以尝试在PC机、51、AVR、430、ARM、上面编译并实验惊讶一下它的工作效率。 前两天有一则新闻大意是说 Ryszard Sommefeldt 很久以前看到这么样的一段 code (可能出自 Quake III 的 source code)
float InvSqrt (float x)
{float xhalf 0.5f*x;int i *(int*)x;i 0x5f3759df - (i1);x *(float*)i;x x*(1.5f - xhalf*x*x);return x;
}他一看之下惊为天人想要拜见这位前辈高人但是一路追寻下去却一直找不到人同时间也有其他人在找虽然也没找到出处但是 Chris Lomont 写了一篇论文 (in PDF) 解析这段 code 的算法 (用的是 Newton’s Method牛顿法比较重要的是后半段讲到怎么找出神奇的 0x5f3759df 的)。 PS. 这个 function 之所以重要是因为求 开根号倒数 这个动作在 3D 运算 (向量运算的部份) 里面常常会用到如果你用最原始的 sqrt() 然后再倒数的话速度比上面的这个版本大概慢了四倍吧… XD PS2. 在他们追寻的过程中有人提到一份叫做 MIT HACKMEM 的文件这是 1970 年代的 MIT 强者们做的一些笔记 (hack memo)大部份是 algorithm有些 code 是 PDP-10 asm 写的另外有少数是 C code (有人整理了一份列表)。 好了故事就到这里结束了希望大家能有有收获:)
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/89184.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!